Spring Boot 全局异常处理

ControllerAdvice + ExceptionHandler

BaseExceptionHandler.java

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
@RestControllerAdvice
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class BaseExceptionHandler {

/**
* 使用 @RequestParam 注解时,如果注解的参数缺失,则会抛出此异常
*/
@ExceptionHandler(value = MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.OK)
public Rs<String> missingServletRequestParameterException(MissingServletRequestParameterException e) {
log.error("缺少请求参数: {}", e.getMessage());
return Rs.error("缺少请求参数");
}

/**
* 使用 @RequestBody 注解时,如果注解的参数为空,则会抛出此异常
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.OK)
Rs<String> httpMessageNotReadableException() {
// Required request body is missing
return Rs.error("缺少 request body 参数");
}

/**
* 统一处理请求参数校验(实体对象传参)
*
* @param e BindException
* @return FebsResponse
*/
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.OK)
public Rs<String> bindException(BindException e) {
StringBuilder message = new StringBuilder();
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
for (FieldError error : fieldErrors) {
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
log.error(message.toString());
return Rs.error(message.toString());
}

/**
* spring + hibernate-validator 验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
Rs<String> methodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
ObjectError error = result.getAllErrors().get(0);
String msg = error.getDefaultMessage();
return Rs.error(msg);
}

/**
* spring + hibernate-validator 验证异常
*/
@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseStatus(HttpStatus.OK)
public Rs<String> constraintViolationException(ConstraintViolationException e) {
StringBuilder message = new StringBuilder();
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
for (ConstraintViolation<?> violation : violations) {
Path path = violation.getPropertyPath();
String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), ".");
message.append(pathArr[1]).append(violation.getMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
log.error(message.toString());
return Rs.error(message.toString());
}

@ExceptionHandler(value = AccessDeniedException.class)
@ResponseStatus(HttpStatus.OK)
public Rs<String> accessDeniedException() {
return Rs.error("没有权限访问该资源");
}

@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
@ResponseStatus(HttpStatus.OK)
public Rs<String> httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
String message = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "媒体类型";
log.error(message);
return Rs.error(message);
}

@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.OK)
public Rs<String> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
String message = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "请求方法";
log.error("msg: {}", message);
return Rs.error(message);
}


// 这里不能最好不要处理 Exception 异常,否则可能会导致页面请求返回错误页面不正确问题
// @ExceptionHandler(value = Exception.class)
// @ResponseStatus(HttpStatus.OK)
// public Rs<String> exception(Exception e) {
// String message = e.getMessage();
// log.error("msg: {}", message, e);
// return Rs.error(message);
// }
}

继承 BasicErrorController

GlobalErrorController.java

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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@ApiIgnore
@Slf4j
public class GlobalErrorController extends BasicErrorController {

public GlobalErrorController(ErrorAttributes errorAttributes,
ServerProperties serverProperties,
List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
}

/**
* 全局异常处理
* <pre>
* 原始异常信息格式
* {
* "timestamp": 1507803567698,
* "status": 404,
* "error": "Not Found",
* "message": "No message available",
* "path": "/v1/api/userinfo"
* }
* </pre>
*
* @param request 请求对象
* @return 返回数据
*/
@Override
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
// 构建一个请求Id
String reqId = String.valueOf(System.currentTimeMillis());
// {timestamp=2021-07-20T08:49:22.134+0800, status=404, error=Not Found, path=/app/listxx}
// {timestamp=2021-07-20T08:49:44.259+0800, status=500, error=Internal Server Error, path=/app/list}
Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
log.error("reqId: {}, body: {}", reqId, body);
// HttpStatus status = this.getStatus(request);
Object status = body.get("status") != null ? body.get("status") : -99;
String error = (String) body.get("error");
String path = (String) body.get("path");

// 获取真实的异常信息
Object attribute = request.getAttribute("javax.servlet.error.exception");
if (attribute instanceof Throwable) {
Throwable throwable = (Throwable) attribute;
String reason = throwable.getMessage();
Throwable cause = getCause(throwable);
String message = cause.getMessage();
if (StringUtils.isNotBlank(message)) {
error = message;
}
if (cause instanceof DataAccessException) {
error = "[" + reqId + "] 执行 sql 语句报错";
} else if (cause instanceof java.sql.SQLSyntaxErrorException) {
error = "[" + reqId + "] " + error;
} else {
error = "[" + reqId + "] " + error;
}
log.error("reqId: {}, reason: {}, msg: {}", reqId, reason, error, throwable);
}

Map<String, Object> map = new HashMap<>(4);
map.put("code", status);
map.put("msg", String.format("error: %s, path: %s, reqId: %s", error, path, reqId));
map.put("success", "0".equals(status));
return new ResponseEntity<>(map, HttpStatus.OK);
}

@Override
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
modelAndView = (modelAndView != null) ? modelAndView : new ModelAndView("error", model);

// 构建一个请求Id
String reqId = String.valueOf(System.currentTimeMillis());

// 获取真实的异常信息
Object attribute = request.getAttribute("javax.servlet.error.exception");
if (attribute instanceof Throwable) {
Throwable throwable = (Throwable) attribute;
String reason = throwable.getMessage();
Throwable cause = getCause(throwable);
String message = cause.getMessage();
if (cause instanceof DataAccessException) {
message = "[" + reqId + "] 执行 sql 语句报错";
} else if (cause instanceof java.sql.SQLSyntaxErrorException) {
message = "[" + reqId + "] " + message;
} else {
message = "[" + reqId + "] " + message;
}
log.error("reqId: {}, reason: {}", reqId, reason, throwable);
if (StringUtils.isNotBlank(message)) {
modelAndView.addObject("msg", message);
}
}
return modelAndView.addObject("reqId", reqId);
}

private Throwable getCause(Throwable cause) {
Throwable cause1 = cause.getCause();
if (cause1 != null) {
return getCause(cause1);
}
return cause;
}
}

错误页面

1
2
3
4
5
6
7
8
9
10
11
<div class="card-body">
<h5 class="card-title">出错啦</h5>
<#if msg??>
<p class="card-text" style="color: #9c9c9c">${msg}</p>
<#else>
<p class="card-text" style="color: #9c9c9c">出错了,请联系管理员或者重试......</p>
</#if>
<#if reqId??>
<p class="card-text" style="color: #9c9c9c">请求 Id:${reqId}</p>
</#if>
</div>
  • 本文作者: forever杨
  • 本文链接: https://blog.yl-online.top/posts/97568726.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。如果文章内容对你有用,请记录到你的笔记中。本博客站点随时会停止服务,请不要收藏、转载!