统一异常处理

概述

对于接口的定义往往规范后会有一个统一的格式如:

1
2
3
4
5
6
{
"code": 200,
"status": true,
"message": "success",
"data": {xxx}
}

但由于用户请求以及代码逻辑运行的不确定性造成的异常,系统可能返回的数据并不是我们规范的格式,甚至返回一个页面。

如访问一个不存在的接口得到:

1
2
3
4
5
6
7
{
"timestamp": "2020-04-25T08:09:43.538+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/testException/2/55"
}

所以我们需要把这些异常返回做集中(统一)式处理。

代码准备

  • CustomException.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //自定义异常类
    public class CustomException extends RuntimeException {

    public CustomException() {
    }

    public CustomException(String message) {
    super(message);
    }
    }
  • ResponseData.class

    1
    2
    3
    4
    5
    6
    7
    8
    //统一返回的数据格式
    public class ResponseData {
    private int code = 200;
    private boolean status = true;
    private String message = null;
    private Object Data = null;
    ...此处省略 get set 方法
    }
  • HelloController.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //用于测试统一异常处理的接口
    @GetMapping("testException/{type}") HelloController
    public String testException(@PathVariable("type") String type ) throws Exception {
    switch (type){
    case "1":
    throw new Exception("default Exception");
    case "2":
    throw new CustomException("custom Exception");
    case "3":
    throw new NullPointerException("null Exception");
    default:
    return "0";
    }
    }
  • GlobalExceptionHandler.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // 统一异常处理类
    @ControllerAdvice
    public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ResponseBody
    @ExceptionHandler(value = CustomException.class)
    /** @ ExceptionHandler({CustomException.class,Exception.class}) 多个异常配置 */
    public ResponseData CustomExceptionHandler(HttpServletRequest request, CustomException e){
    logger.info(e.getMessage());
    ResponseData responseData = new ResponseData();
    responseData.setCode(405);
    responseData.setMessage(e.getMessage());
    responseData.setStatus(false);
    return responseData;
    }

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ResponseData defaultExceptionHandler(HttpServletRequest request, Exception e){
    logger.info(e.getMessage());
    ResponseData responseData = new ResponseData();
    if (e instanceof NullPointerException){
    responseData.setCode(407);
    }else {
    responseData.setCode(406);
    }
    responseData.setMessage(e.getMessage());
    responseData.setStatus(false);
    return responseData;
    }
    }

测试

请求:http://localhost:8082/testException/1

响应:

1
2
3
4
5
6
{
"code": 406,
"status": false,
"message": "default Exception",
"data": null
}

请求:http://localhost:8082/testException/2

响应:

1
2
3
4
5
6
{
"code": 405,
"status": false,
"message": "custom Exception",
"data": null
}

请求:http://localhost:8082/testException/3

响应:

1
2
3
4
5
6
{
"code": 407,
"status": false,
"message": "null Exception",
"data": null
}

请求:http://localhost:8082/testException/3/xxx (没有此接口)

响应:

1
2
3
4
5
6
7
{
"timestamp": "2020-04-25T08:30:12.922+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/testException/3/xxx"
}

发现请求不存在的接口返回的不是我们统一的格式我们需要程序对这种不存在的资源不能映射到404

增加配置

  • application.properties

    1
    2
    3
    4
    #出现错误时 直接抛出异常
    spring.mvc.throw-exception-if-no-handler-found=true
    #不对资源文件做映射
    spring.resources.add-mappings=false

    再次请求得到响应:

    1
    2
    3
    4
    5
    6
    {
    "code": 406,
    "status": false,
    "message": "No handler found for GET /testException/3/xxx",
    "data": null
    }