使用 @ControlerAdvice 和 @ExceptionHandler 通用处理异常

使用原因

Spring Boot 默认错误机制会返回一个Whitelabel Error Page 的错误页面,不够友好,所以使用 @ControllerAdvice@ExceptionHandler 来通用处理异常。

实现步骤

  1. 新建一个通用处理异常的类 CustomizeExceptionHandler,集中处理异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ControllerAdvice
public class CustomizeExceptionHandler {

@ExceptionHandler(Exception.class) // 捕获异常
ModelAndView handle(Throwable e, Model model) {
// 判断处理,写入Model
if (e instanceof CustomizeException) {
model.addAttribute("message", e.getMessage());
} else {
model.addAttribute("message", "服务器异常");
}
return new ModelAndView("error");
}
}
  1. 继承自RuntimeException的异常类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CustomizeException extends RuntimeException {
private String message;

public CustomizeException(ICustomizeErrorCode errorCode) {
this.message = errorCode.getMessage();
}

public CustomizeException(String message) {
this.message = message;
}

@Override
public String getMessage() {
return message;
}
}
  1. 自定义一个错误代码的接口。
1
2
3
4
public interface ICustomizeErrorCode {
String getMessage();
Integer getCode();
}
  1. 实现ICustomizeErrorCode接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public enum CustomizeErrorCode implements ICustomizeErrorCode {

QUESTION_NOT_FOUND("你找到问题不在了,要不要换个试试?");

@Override
public String getMessage() {
return message;
}

private String message;

CustomizeErrorCode(String message) {
this.message = message;
}


}
  1. Controller层实现默认的ErrorController,替代ErrorController默认的错误处理操作。

    CustomizeErrorController 捕获到4**错误时,返回客户端请求错误的信息,5**错误时返回服务器错误信息。

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
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class CustomizeErrorController implements ErrorController {

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

@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, Model model) {
HttpStatus status = getStatus(request);

if (status.is4xxClientError()) {
model.addAttribute("message", "客户端请求错误");
}
if (status.is5xxServerError()) {
model.addAttribute("message", "服务器异常");
}

return new ModelAndView("error");
}

private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request
.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
try {
return HttpStatus.valueOf(statusCode);
} catch (Exception ex) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
}
  1. templates目录下新建error.html页面,用来显示错误处理。

    cssjs 的引入一定要在前面加个/,表示是在根目录下的样式,否则会出现样式找不到的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>小智社区</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/css/community.css">
<script src="/js/jquery-3.4.1.min.js"></script>
<script src="/js/bootstrap.min.js" type="application/javascript"></script>
</head>
<body>
<div th:insert="~{navigation :: nav}"></div>
<div class="jumbotron container">
<h1>出错啦~</h1>
<p th:text="${message}">服务器承受不住啦~请稍后再来!</p>
<p><a class="btn btn-primary btn-lg" href="/" role="button">点击返回首页</a></p>
</div>
</body>
</html>
  1. service层使用。
1
2
3
4
Question question = questionMapper.selectByPrimaryKey(id);
if (question == null) {
throw new CustomizeException(CustomizeErrorCode.QUESTION_NOT_FOUND);
}
  1. 结果如图所示。

错误页面

参考资料 Spring Boot 文档——Error Handling