Spring Boot - 全局异常处理
# 前言
处理业务逻辑的时候,当出现一些中断的操作,最简单的就是直接返回一个简单的「消息」,但是代码链路很长的时候,这是一个不优雅的处理,而直接抛出异常,然后绕过这些代码链路,直接走到 Controller 返回给前端,是一个推荐的写法。
Spring Boot 提供了一个全局异常的捕获处理机制,我们只需要使用它写出我们需要捕获的异常并处理,然后将处理的结果返回给前端即可。
处理办法如下:通过使用 @ControllerAdvice
来进行统一异常处理,@ExceptionHandler(value = RuntimeException.class)
来指定捕获的 Exception 各个类型异常 ,这个异常的处理,是全局的,所有类似的异常,都会跑到这个地方处理。
# 使用
定义一个
通用
下面给出常用的异常处理方法,如果有自己的业务异常,则自行捕获即可。
部分异常使用了如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
非常好理解,出现对应的异常就会走到对应的处理方法,最后返回给前端。
返回给前端的结果响应类 Response
和处理类 HttpResult
看:Spring Boot - 通用响应类
@RestControllerAdvice
@ResponseBody
@Slf4j
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Response<Object> handleError(MissingServletRequestParameterException e) {
log.warn("缺少请求参数:{}", e.getMessage());
String message = String.format("缺少必要的请求参数: %s", e.getParameterName());
return HttpResult.error(ResponseStatusEnum.PARAM_MISS, message);
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Response<Object> handleError(MethodArgumentTypeMismatchException e) {
log.warn("请求参数格式错误:{}", e.getMessage());
String message = String.format("请求参数格式错误: %s", e.getName());
return HttpResult.fail(ResponseStatusEnum.PARAM_TYPE_ERROR, message);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Response<Object> handleError(MethodArgumentNotValidException e) {
log.warn("参数验证失败:{}", e.getMessage());
return handleError(e.getBindingResult());
}
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Response<Object> handleError(BindException e) {
log.warn("参数绑定失败:{}", e.getMessage());
return handleError(e.getBindingResult());
}
private Response<Object> handleError(BindingResult result) {
FieldError error = result.getFieldError();
String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
return HttpResult.fail(ResponseStatusEnum.PARAM_BIND_ERROR, message);
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Response<Object> handleError(ConstraintViolationException e) {
log.warn("参数验证失败:{}", e.getMessage());
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String path = ((PathImpl) violation.getPropertyPath()).getLeafNode().getName();
String message = String.format("%s:%s", path, violation.getMessage());
return HttpResult.fail(ResponseStatusEnum.PARAM_VALID_ERROR, message);
}
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Response<Object> handleError(NoHandlerFoundException e) {
log.error("404 没找到请求:{}", e.getMessage());
return HttpResult.fail(ResponseStatusEnum.NOT_FOUND, e.getMessage());
}
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Response<Object> handleError(HttpMessageNotReadableException e) {
log.error("消息不能读取:{}", e.getMessage());
return HttpResult.fail(ResponseStatusEnum.MSG_NOT_READABLE, e.getMessage());
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public Response<Object> handleError(HttpRequestMethodNotSupportedException e) {
log.error("不支持当前请求方法:{}", e.getMessage());
return HttpResult.error(ResponseStatusEnum.METHOD_NOT_SUPPORTED, e.getMessage());
}
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public Response<Object> handleError(HttpMediaTypeNotSupportedException e) {
log.error("不支持当前媒体类型:{}", e.getMessage());
return HttpResult.error(ResponseStatusEnum.MEDIA_TYPE_NOT_SUPPORTED, e.getMessage());
}
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public Response<Object> handleError(HttpMediaTypeNotAcceptableException e) {
log.error("媒体类型异常:{}", e.getMessage());
return HttpResult.error(ResponseStatusEnum.MEDIA_TYPE_NOT_SUPPORTED, e.getMessage());
}
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Response<Object> handleError(Throwable e) {
log.error("服务器异常", e);
// 发送服务异常事件
return HttpResult.error(ResponseStatusEnum.INTERNAL_SERVER_ERROR, (Objects.isNull(e.getMessage()) ? ResponseStatusEnum.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage()));
}
/**
* 处理系统异常,兜底处理所有的一切
*/
@ExceptionHandler(value = Exception.class)
public Response<Object> defaultExceptionHandler(Throwable e) {
log.error("服务器异常", e);
// 返回 ERROR CommonResult
return HttpResult.error(ResponseStatusEnum.INTERNAL_SERVER_ERROR);
}
}
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
编辑此页 (opens new window)
更新时间: 2023/09/18, 16:34:13