自定义错误页
1.源码解析
在SpringBoot中,默认情况下,如果用户在发起请求时发生了404错误,SpringBoot会有一个默认页面展示给用户。
SpringBoot在返回错误信息时不一定返回HTML页面,而是根据实际情况返回 HTML页面或者一段 JSON。
Spring Boot 中的错误默认是由 BasicErrorController 类来处理的 ,该类中的核心方法主要有两个:
@RequestMapping(
produces = {"text/html"}
)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = this.getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity(status);
} else {
Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity(body, status);
}
}
其中,errorHtml方法用来返回错误HTML页面,error用来返回错误JSON,具体返回的是HTML还是JSON,则要看请求头的Accept参数。在errorHTML方法中,通过调用resolveErrorView方法来获取一个错误视图的ModelAndView。而 resolveErrorView 方法的调用最终会来到 DefaultErrorViewResolver 类中 。 DefaultErrorViewResolver 类是 Spring Boot 中默认的错误信息视图解析器, 部分源码如下:
static {
Map<Series, String> views = new EnumMap<>(Series.class);
views.put(Series.CLIENT_ERROR, "4xx");
views.put(Series.SERVER_ERROR, "5xx");
SERIES_VIEWS = Collections.unmodifiableMap(views);
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
this.applicationContext);
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model);
}
SpringBoot默认是在error目录下查找4xx、5xx的文件作为错误视图,党找不到时会回到errorHtml方法中,然后使用error作为默认的错误页面视图名。
2.简单配置
要自定义错误页面,只需要提供4xx和5xx页面。如果不需要向用户展示详细的错误信息,可以把错误信息定义为静态页面,在resources/static目录下创建error目录。错误展示页面的命名规则有两种:4xx.hmtl、5xx.html,还可以404.html、405.html、500.html
当用户访问一个不存在的路径时,就会展示404.html的内容
这种定义都是使用了静态HTML页面,无法向用户展示完整的错误信息,若采用视图模板技术,则可以向用户展示更多的错误信息。如果要使用Thymeleaf模板,要先引入Thymeleaf模板相关的依赖。Thymeleaf 页面模板默认处于= classpath: /templates/目录下,因此在该目录下先创建 error 目录,再创建错误展示页。
页面代码:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1">
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
</table>
</body>
</html>
SpringBoot在这里返回了5条错误相关信息,分别是timestamp, status 、 error 、 message、path。
3.复杂配置
SpringBoot中支持对Error信息的深度定制,有三个方面:自定义Error数据、自定义Eoor视图、完全自定义。
3.1 自定义Error数据
自定义Error数据就是对返回的数据进行自定义。在BasicErrorController的errorHtml和error方法中,都是通过getErrorAttributes方法获取Error信息。该方法最终会调用到DefaultErrorAttributes 类的 getErrorAttributes 方法,而DefaultError Attributes 类是在 ErrorMvcAutoConfiguration 中默认提供的 。ErrorMvcAutoConfiguration
类的 errorAttributes 方法源码如下:
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes();
}
从源码中可以看出,当系统没有提供ErrorAttributes时会采用DefaultErrorAttributes。定义错误提示使,只需要自己提供ErrorAttributes,而 DefaultErrorAttributes 是Error Attributes 的子类,需要继承DefaultErrorAttributes
@Component
public class MyErrorAttribute extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
errorAttributes.put("custommsg","出错啦");
errorAttributes.remove("error");
return errorAttributes;
}
代码解释:
- List item自定义MyErrorAttribute 继承自DefaultErrorAttributes,重写DefaultErrorAttributes中的getErrorAttributes方法。
- 通过super.getErrorAttributes获取SpringBoot默认提供的错误信息。
页面代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1">
<tr>
<td>custommsg</td>
<td th:text="${custommsg}"></td>
</tr>
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
</table>
</body>
</html>
在第 9~ 12 行添加了 custommsg 属性。
3.2 自定义Error视图
Error 视图是展示给用户的页面,在BasicErrorController的errorHtml方法中调用resolveErrorView方法获取一个ModelAndView实例。resolveErrorView方法是由ErrorView Resolver 提供的。ErrorMvcAutoConfiguration类的源码可以看到默认采用的ErrorViewResolver是DefaultErrorViewResolver。ErrorMvcAutoConfiguration 源码如下:
@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean(ErrorViewResolver.class)
DefaultErrorViewResolver conventionErrorViewResolver() {
return new DefaultErrorViewResolver(this.applicationContext, this.resources);
}
从源码看出,如果用户没有定义ErrorViewResolver,那么默认使用的ErrorViewResolver是DefaultErrorViewResolver,正是在DefaultErrorViewResolver中默认配置了默认去error目录下寻找4xx.html。自定义Error视图,只需要提供自己的ErrorViewResolver即可。
@Component
public class MyErrorAttribute extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
errorAttributes.put("custommsg","出错啦");
errorAttributes.remove("error");
return errorAttributes;
}
}
代码解释:
- 自定义MyErrorViewResolver实现 ErrorView Resolver 接口并实现接口中的resolveErrorView方法。
- 在 resolveErrorView 方法中 最后一个 Map参数就是 Spring Boot 提供的默认的 5 条 Error信息,在 resolveErrorView方法中,返回一个 ModelAndVie趴在 ModelAndView 中设直 Error视图和 Error数据。
接下来在 resources/templates 目录下提供 errorPage.html 视图 ,内容如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>errorPage</title>
</head>
<body>
<table border="1">
<tr>
<td>custommsg</td>
<td th:text="${custommsg}"></td>
</tr>
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
</table>
</body>
</html>
在 errorPage.html 除了展示 Spring Boot 提供的 5 条 Error信息外,也展示了开发者自定义的 Error 信息。此时,无论请求发生 4xx 的错误还是发生 5xx 的错误,都会来到 errorPage .html页面。