Spring Boot全局異常捕獲處理

目標:通過ControllerAdvice和ExceptionHandler捕獲異常和錯誤信息,向前端返回json格式的狀態碼及異常描述信息。

1.創建一個全局異常捕獲處理類GlobalExceptionHandler

package com.qiqi.exception;
import com.qiqi.utils.ExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 全局異常捕獲處理
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * http請求的方法不正確
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public String httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
        logger.error("http請求的方法不正確:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.RequestMethodNotAllowed);
    }

    /**
     * 請求參數不全
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public String missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
        logger.error("請求參數不全:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.MissingServletRequestParameter);
    }

    /**
     * 請求參數類型不正確
     */
    @ExceptionHandler(TypeMismatchException.class)
    @ResponseBody
    public String typeMismatchExceptionHandler(TypeMismatchException e) {
        logger.error("請求參數類型不正確:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.TypeMismatchException);
    }

    /**
     * 數據格式不正確
     */
    @ExceptionHandler(DataFormatException.class)
    @ResponseBody
    public String dataFormatExceptionHandler(DataFormatException e) {
        logger.error("數據格式不正確:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.DataFormatException);
    }

    /**
     * 用戶沒找到
     */
    @ExceptionHandler(UserNotFoundException.class)
    @ResponseBody
    public String userNotFoundExceptionHandler(UserNotFoundException e) {
        logger.error("用戶沒找到:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.UserNotExist);
    }

    /**
     * 非法輸入
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public String illegalArgumentExceptionHandler(IllegalArgumentException e) {
        logger.error("非法輸入:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.IllegalArgumentException);
    }


    @ExceptionHandler  //處理其他異常
    @ResponseBody
    public String allExceptionHandler(Exception e){
        logger.error("具體錯誤信息:【"+ExceptionUtil.getErrorMessage(e)+"】"); //會記錄出錯的代碼行等具體信息
        int count = 0; //只打印15行的錯誤堆棧
        for (StackTraceElement stackTraceElement : e.getStackTrace()) {
            logger.error(stackTraceElement.toString());
            if(count++ > 13) break;
        }
        return ExceptionUtil.resultOf(ResultStatusCode.SystemException);
    }

}

2.自定義了一些異常UserNotFoundException、DataFormatException等。

public class UserNotFoundException extends Exception {
    public UserNotFoundException() {
    }

    public UserNotFoundException(String message) {
        super(message);
    }

    public UserNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

 3.自定義一個異常狀態碼的枚舉類 ResultStatusCode

public enum ResultStatusCode {

    Success("0", "Success"),
    UserNotExist("1", "User not exist"),
    InValidParameter("2","Invalid parameter"),
    DataFormatException("4", "DataFormat exception"),
    DataNotExistException("5", "DataNotExistException"),
    TimeFormatException("6","TimeFormat Exception"),
    PictureFormatException("7","PictureFormat Exception"),
    IllegalArgumentException("8","IllegalArgumentException"),
    TokenInvalidOrOverdueException("9", "Token invalid or overdue exception"),
    AuthorizationCodeError("10", "authorization code error"),
    WrongSignatureException("11","Wrong Signature Exception"),
    SystemException("50", "system Exception"),
    MissingServletRequestParameter("400","Missing servletRequest parameter"),
    TypeMismatchException("401","Request parameter Type not match"),
    RequestMethodNotAllowed("405","Request method  not Allowed"),
    ;

    private String code;
    private String msg;

    private ResultStatusCode(String code,String msg){
        this.code=code;
        this.msg=msg;
    }

    public String getMsg(){
        return this.msg;
    }
    public String getCode(){
        return this.code;
    }

}

4.自定義異常處理工具類

裏面編寫兩個方法:

方法一:打印異常的詳細信息

方法二:將枚舉類型的狀態碼及描述轉爲json

注意:fastjson包,必須爲1.2.24,否則沒有configEnumAsJavaBean()方法。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.qiqi.exception.ResultStatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 返回異常的處理
 */
public class ExceptionUtil {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);

    private ExceptionUtil() {
    }

    /**
     * 獲取異常信息
     *
     * @param e 異常
     */
    public static String getErrorMessage(Exception e) {
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            // 將出錯的棧信息輸出到printWriter中
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
        } finally {
            if (sw != null) {
                try {
                    sw.close();
                } catch (IOException e1) {
                    logger.info(e1.getMessage());
                }
            }
            if (pw != null) {
                pw.close();
            }
        }
        return sw.toString();
    }
    /**
     * 異常信息-->json
     *
     */
    public static String resultOf(ResultStatusCode resultStatusCode) {
        SerializeConfig config = new SerializeConfig();
        config.configEnumAsJavaBean(ResultStatusCode.class);
        return JSON.toJSONString(resultStatusCode, config);
    }

}

5.使用

Controller層:拋出異常讓GlobalExceptionHandler捕獲處理

    /**
     * 刪除用戶
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public Response deleteUser(@RequestParam(value = "user_id", required = true) String userId)
            throws UserNotFoundException {
        Response response = new Response();
        long id = Long.parseLong(userId);
        userService.deleteUser(id);
        response = new Response("0","刪除用戶成功",null);
        logger.info("刪除用戶:id="+ userId + "  result="+response.toString());
        return response;
    }

Service層: 拋出異常

    public void deleteUser(long userId )throws UserNotFoundException {
        if (userRepository.existsById(userId) == false){
             throw new UserNotFoundException();
        }
        userRepository.deleteById(userId);
    }

6.測試結果

一:填寫請求參數時,故意讓String類型的user_id =“3a",在long id = Long.parseLong(userId);轉換時,會拋出IllegalArgumentException異常的子類NumberFormatException。由於我們在GlobalExceptionHandler捕獲處理了IllegalArgumentException這個非法輸入異常,並對其定義了狀態碼及描述。所以前端會收到json格式的響應:

{"code":"8","msg":"IllegalArgumentException"}

而在控制檯,會輸出日誌:2019-01-04 14:39:21.437 ERROR com.qiqi.exception.GlobalExceptionHandler Line:118 - 非法輸入:【For input string: "3a"】

二:請求時發送POST請求(代碼中要求是GET請求)

前端響應:{"code":"405","msg":"Request method  not Allowed"}

後臺日誌:2019-01-04 14:41:41.963 ERROR com.qiqi.exception.GlobalExceptionHandler Line:58  - http請求的方法不正確:【Request method 'POST' not supported】

三:在程序中拋出IOException

IOException異常沒有單獨捕獲,被捕獲在allExceptionHandler方法中,在這個方法中打印出了異常的詳細信息,包括出現異常的代碼行號等,同時還打印了異常的堆棧信息。

前端響應:{"code":"50","msg":"system Exception"}

後臺日誌:

2019-01-04 14:57:08.583 ERROR com.qiqi.exception.GlobalExceptionHandler Line:145 - 具體錯誤信息:【java.io.IOException
	at com.qiqi.service.UserService.getUser(UserService.java:42)
	at com.qiqi.controller.UserController.getUser(UserController.java:47)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)
】
2019-01-04 14:57:08.587 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - com.qiqi.service.UserService.getUser(UserService.java:42)
2019-01-04 14:57:08.587 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - com.qiqi.controller.UserController.getUser(UserController.java:47)
2019-01-04 14:57:08.587 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - java.lang.reflect.Method.invoke(Method.java:497)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章