简单配置
第一种情况:不使用模板
如果不需要向用户展示详细的错误信息,那么可以把错误信息定义成静态页面,直接在resources/static目录下创建error目录,然后在error目录中创建错误展示页面。
错误展示页面的规则有两种:
- 4xx.html、5xx.html
- 直接使用相应码命名文件,例如404.html、500.html
第二种情况:使用模板
如果要使用HTML模板,以Thymeleaf为例,Thymeleaf页面模板默认处于**classpath:/templates/**下,因此在该目录下创建error目录,再创建错误页面即可,规则和上面的一致。
示例:使用HTML模板
错误页面:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
404<br/>
<p th:text="${timestamp}"></p>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
500<br/>
<p th:text="${timestamp}"></p>
</body>
</html>
提供一个会抛异常的Controller
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(){
int i = 1 / 0;
return "hello";
}
}
测试404:
测试500:
注意:
- 如果定义了多个错误页面,则响应码.html页面的优先级高于4xx.html、5xx.html页面的优先级;
- 动态页面的优先级高于静态页面的优先级;
复杂配置
1. 自定义Error数据
SpringBoot返回的Error信息一共有5条,分别是timestamp、status、error、message以及path。在BasicErrorController的errorHtml方法和error方法中,都是通过getErrorAttributes方法获取Error信息的。该方法最终会调用到DefaultErrorAttributes类的getErrorAttributes方法,而DefaultErrorAttributes类是在ErrorMvcAutoConfiguration中默认提供的。
ErrorMvcAutoConfiguration类的errorAttributes方法源码如下:
@Bean
@ConditionalOnMissingBean(
value = {ErrorAttributes.class},
search = SearchStrategy.CURRENT
)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(
this.serverProperties.getError().isIncludeException());
}
可以看出,当系统没有提供ErrorAttributes时,才会采用DefaultErrorAttributes。
所以,我们只需要提供一个自己的ErrorAttributes即可,而DefaultErrorAttributes是ErrorAttributes的子类,因此只需要继承DefaultErrorAttributes,重写getErrorAttributes方法即可。
实例:
@Component
public class MyErrorAttibute extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
map.put("msg","粗错啦!");//增加自定义信息
map.remove("error");//删除error信息
return map;
}
}
测试500:
测试404:
注意: 返回的JSON的数据也会随之改变。
自定义Error视图
在BasicErrorController的errorHtml方法中调用resolveErrorView方法获取一个ModelAndView实例。resolveErrorView方法是由ErrorViewResolver提供的。
@Bean
@ConditionalOnBean({DispatcherServlet.class})
@ConditionalOnMissingBean
public DefaultErrorViewResolver conventionErrorViewResolver() {
return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
}
从源码中可以看出,如果我们没有定义ErrorViewResolver,那么默认使用的ErrorViewResolver是DefaultErrorViewResolver,所以如果想自定义Error视图,只需要提供自己的ErrorViewResolver即可。
@Component
public class MyErrorViewResolver implements ErrorViewResolver {
@Override
public ModelAndView resolveErrorView(HttpServletRequest httpServletRequest, HttpStatus httpStatus, Map<String, Object> map) {
ModelAndView modelAndView = new ModelAndView("/error/errorPage");
modelAndView.addObject("xxx","resolverErrorView中添加的数据");
modelAndView.addAllObjects(map);//系统定义的数据
return modelAndView;
}
}
- 自定义MyErrorViewResolver实现ErrorViewResolverResolver接口并实现接口中的resolverErrorView方法,使用@Component注解将该类注册到Spring容器中。
- 在resolveErrorView方法中,最后一个Map参数就是SpringBoot提供的默认的5条Error信息(可以按照前面自定义的步骤对这5条消息进行修改)。在resolveErrorView方法中,返回一个ModelAndView,在ModelAndView中设置Error视图和Error数据。
- 理论上,开发者也可以通过实现ErrorResolver接口来实现Error数据的自定义,但如果只是单纯的想自定义Error数据,建议京城DefaultErrorAttributes。
页面:errorPage
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h1>自定义错误视图</h1>
<p th:text="${timestamp}"></p><br/>
<p th:text="${status}"></p><br/>
<p th:text="${error}"></p><br/>
<p th:text="${message}"></p><br/>
<p th:text="${path}"></p><br/>
<p th:text="${msg}"></p><br/>
<p th:text="${xxx}"></p><br/>
</body>
</html>
测试:
注意: 此时无论请求发生4xx的错误还是发生5xx的错误,都会跳转到errorPage.html页面。
完全自定义
前面的两种方式都是对BasicErrorController类中的某个环节进行修补。查看Error自动化配置类ErrorMvcConfiguration,可以发现BasicErrorController本身只是一个默认的配置:
@Bean
@ConditionalOnMissingBean(
value = {ErrorController.class},
search = SearchStrategy.CURRENT
)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}
可以看出,若开发者没有提供自己的ErrorController,则SpringBoot提供BasicErrorController作为默认的ErrorController。因为,如果想完全自定义错误处理,只需要提供一个子集的ErrorController即可。
提供自己的ErrorController有两种方式:
- 实现ErrorController接口
- 继承BasicErrorController
因为BasicErrorController已经实现了很多功能,所以选择继承BasicErrorController。
@Controller
public class MyErrorController extends BasicErrorController {
@Autowired
public MyErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
}
@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> map = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
map.put("myError", "myError");
ModelAndView modelAndView = new ModelAndView("/error/myErrorPage", map, status);
return modelAndView;
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> map = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
map.put("myError", "myError");
HttpStatus status = getStatus(request);
return new ResponseEntity<>(map, status);
}
}
- 自定义MyErrorController继承BasicErrorController并添加@Controller注解,注册到容器中。
- 由于BasicErrorController没有无参构造方法,因此在创建BasicErrorController实例的时候需要传递参数,在MyErrorController的构造方法上添加@Autowired注解注入参数。
- 重写errorHtml和error方法。
页面:myErrorPage
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h1>完全自定义</h1>
<p th:text="${timestamp}"></p><br/>
<p th:text="${status}"></p><br/>
<p th:text="${error}"></p><br/>
<p th:text="${message}"></p><br/>
<p th:text="${path}"></p><br/>
<p th:text="${msg}"></p><br/>
<p th:text="${xxx}"></p><br/>
<p th:text="${myError}"></p>
</body>
</html>
测试: