SpringBoot统一异常处理

版权申明:本文为原创文章,转载请注明原文出处

原文链接:https://blog.it-follower.com/posts/4006834737.html

​ 为了编码SpringBoot项目开发过程中代码里出现大量的try..catch…,我们常常会开发一个统一异常处理,开发者只需专注于业务开发即可,不用再去关系异常如何处理。

​ 实际项目开发中,会遇到两种类型的异常,这里分开来讨论

一、 请求正确被分发了,但是处理请求过程中,出现异常。

​ 这种情况,我们可以自定义一个Controller增强类,并加上@ControllerAdvice,要确保该注解能被Spring自动扫描到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Slf4j
@ControllerAdvice
public class BaseController {

/**
* 系统异常处理
*
* @param request Http请求
* @param ex 异常对象
*/
@ExceptionHandler(Exception.class)
@ResponseBody
protected ApiResp exceptionProcess(HttpServletRequest request, Exception ex) {

log.error("[System Exception] url: {}", request.getRequestURL().toString(), ex);
return ApiResp
.retFail(BaseError.SYSTEM_EXCEPTION.getCode(), BaseError.SYSTEM_EXCEPTION.getMsg());
}
}

注意,如果是返回JSON的数据格式,该处理方法需要加上@ResponseBody注解。

另外,除了最后兜底的Exception外,还可以自定义特殊处理一些其他的异常,已给出更加精确的返回结果,例如,参数校验异常等等。

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
/**
* 参数校验异常
*
* @param request Http请求
* @param ex 异常对象
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
protected ApiResp exceptionProcess(HttpServletRequest request, MethodArgumentNotValidException ex) {

List<ObjectError> list = ex.getBindingResult().getAllErrors();
List<String> detailMsg = Lists.newArrayList();
for (ObjectError objectError : list) {
detailMsg.add(objectError.getDefaultMessage());
}

int code = BaseError.SYSTEM_ARGUMENT_NOT_VALID.getCode();
String msg = BaseError.SYSTEM_ARGUMENT_NOT_VALID.getMsg();
ArgumentNotValidMsg data = new ArgumentNotValidMsg();
data.setDetailMsg(detailMsg);

log.info("[Method Args Exception] URL: {}, ERROR: {}", request.getRequestURL().toString(),
StringUtils.join(detailMsg, ","));
return ApiResp.retFail(code, msg, data);

}

这样配置后,当出现参数校验不通过时,提示信息更明确,效果如下:

1
2
3
4
5
6
7
8
9
10
{
"code": 10003,
"msg": "参数校验失败",
"data": {
"detailMsg": [
"password最小长度6位",
"username不能为空"
]
}
}

二、 第二种情况是,请求没有被正确分发,比如请求地址404,或者权限不足等等。 (可选配置)

这种情况下,SpringBoot内置了一个BasicErrorController来进行处理。但是其返回格式不满足我们的项目要求,所以实现ErrorController,重写里面的处理方法。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@RestController
@RequestMapping("/error")
public class ErrorHandleController implements ErrorController {

private final ErrorAttributes errorAttributes;

private final ServerProperties serverProperties;

@Autowired
public ErrorHandleController(ErrorAttributes errorAttributes,
ServerProperties serverProperties) {
this.errorAttributes = errorAttributes;
this.serverProperties = serverProperties;
}

@RequestMapping("")
public ApiResp error(HttpServletRequest request, HttpServletResponse response) {

Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request));

response.setStatus(HttpStatus.OK.value());

int code = (int) body.get("status");
String msg = "'" + body.get("path") + "' " + body.get("error");
return ApiResp.retFail(code, msg);
}

@Override
public String getErrorPath() {
return "/error";
}

private Map<String, Object> getErrorAttributes(HttpServletRequest request,
boolean includeStackTrace) {

WebRequest webRequest = new ServletWebRequest(request);
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}

private boolean isIncludeStackTrace(HttpServletRequest request) {

ErrorProperties.IncludeStacktrace include = this.serverProperties.getError().getIncludeStacktrace();
if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
return true;
}
if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}

private boolean getTraceParameter(HttpServletRequest request) {

String parameter = request.getParameter("trace");
if (parameter == null) {
return false;
}
return !"false".equals(parameter.toLowerCase());
}
}

处理前的效果:

1
2
3
4
5
6
7
{
"timestamp": "2019-01-01 12:12:36",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/hello"
}

处理后的效果:

1
2
3
4
5
{
"code": 404,
"msg": "'/hello' Not Found",
"data": null
}

具体内容可参照我的github项目https://github.com/ye17186/myhelper-spring-boot-starter

评论