静态异常页面
自定义静态异常页面,又分为两种,第一种 是使用 HTTP 响应码来命名页面,例如 404.html、405.html、500.html
….,另一种就是直接定义一个 4xx.html
,表示400-499 的状态都显示这个异常页面,5xx.html 表示 500-599 的状态显示这个异常页面。
默认是在 classpath:/static/error/
路径下定义相关页面:
此时,启动项目,如果项目抛出 500 请求错误,就会自动展示 500.html 这个页面,发生 404 就会展示 404.html 页面。如果异常展示页面既存在 5xx.html,也存在 500.html ,此时,发生500异常时,优先展示 500.html 页面
动态异常页面
动态的异常页面定义方式和静态的基本 一致,可以采用的页面模板有 jsp
、freemarker
、thymeleaf
。动态异常页面,也支持 404.html 或者 4xx.html
,但是一般来说,由于动态异常页面可以直接展示异常详细信息,所以就没有必要挨个枚举错误了 ,直接定义 4xx.html
(这里使用thymeleaf模板)或者 5xx.html 即可。
注意,动态页面模板,不需要开发者自己去定义控制器,直接定义异常页面即可 ,Spring Boot 中自带的异常处理器会自动查找到异常页面。
页面定义如下:
页面内容如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>5xx</h1>
<table border="1">
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
</table>
</body>
</html>
默认情况下,完整的异常信息就是这5条,展示效果如下:
如果动态页面和静态页面同时定义了异常处理页面,例如 classpath:/static/error/404.html
和 classpath:/templates/error/404.html
同时存在时,默认使用动态页面。即完整的错误页面查找方式应该是这样:
发生了500错误–>查找动态 500.html 页面–>查找静态 500.html –> 查找动态 5xx.html–>查找静态 5xx.html。
总结:动态的>静态的,精准的>模糊的
自定义异常数据
默认情况下,在Spring Boot 中,所有的异常数据其实就是上文所展示出来的5条数据,这5条数据定义在 org.springframework.boot.web.reactive.error.DefaultErrorAttributes
类中,具体定义在 getErrorAttributes
方法中 :
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
errorAttributes.put("path", request.path());
Throwable error = getError(request);
HttpStatus errorStatus = determineHttpStatus(error);
errorAttributes.put("status", errorStatus.value());
errorAttributes.put("error", errorStatus.getReasonPhrase());
errorAttributes.put("message", determineMessage(error));
handleException(errorAttributes, determineException(error), includeStackTrace);
return errorAttributes;
}
DefaultErrorAttributes 类本身则是在org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
异常自动配置类中定义的,如果开发者没有自己提供一个 ErrorAttributes
的实例的话,那么 Spring Boot 将自动提供一个ErrorAttributes
的实例,也就是 DefaultErrorAttributes
。
基于此 ,开发者自定义 ErrorAttributes
有两种方式 :
- 直接实现
ErrorAttributes
接口 - 继承
DefaultErrorAttributes
(推荐),因为DefaultErrorAttributes
中对异常数据的处理已经完成,开发者可以直接使用。
具体定义如下:
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
if ((Integer)map.get("status") == 500) {
map.put("message", "服务器内部错误!");
}
return map;
}
}
定义好的 ErrorAttributes
一定要注册成一个 Bean ,这样,Spring Boot 就不会使用默认的 DefaultErrorAttributes
了,运行效果如下图:
自定义异常视图
异常视图默认就是前面所说的静态或者动态页面,这个也是可以自定义的,首先 ,默认的异常视图加载逻辑在 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController
类的 errorHtml
方法中,这个方法用来返回异常页面+数据,还有另外一个 error
方法,这个方法用来返回异常数据(如果是 ajax 请求,则该方法会被触发)
@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, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
在该方法中 ,首先会通过 getErrorAttributes
方法去获取异常数据(实际上会调用到 ErrorAttributes
的实例 的 getErrorAttributes
方法),然后调用 resolveErrorView
去创建一个 ModelAndView
,如果这里创建失败,那么用户将会看到默认的错误提示页面。
正常情况下, resolveErrorView
方法会来到 DefaultErrorViewResolver
类的 resolveErrorView
方法中:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
在这里,首先以异常响应码作为视图名分别去查找动态页面和静态页面,如果没有查找到,则再以 4xx
或者 5xx
作为视图名再去分别查找动态或者静态页面
要自定义异常视图解析,也很容易 ,由于 DefaultErrorViewResolver
是在 ErrorMvcAutoConfiguration
类中提供的实例,即开发者没有提供相关实例时,会使用默认的 DefaultErrorViewResolver
,开发者提供了自己的 ErrorViewResolver
实例后,默认的配置就会失效,因此,自定义异常视图,只需要提供 一个 ErrorViewResolver
的实例即可:
@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {
super(applicationContext, resourceProperties);
}
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
return new ModelAndView("/aaa/123", model);
}
}
实际上,开发者也可以在这里定义异常数据(直接在 resolveErrorView
方法重新定义一个 model
,将参数中的model 数据拷贝过去并修改,注意参数中的 model 类型为 UnmodifiableMap
,即不可以直接修改),而不需要自定义MyErrorAttributes
。定义完成后,提供一个名为123的视图,如下图:
如此之后,错误视图就算定义成功了。