SpringBoot错误处理机制
SpringBoot默认的错误处理机制
当发生错误时,针对不同的客户端,SpringBoot有不同的处理。如果是浏览器,就会返回默认的错误页面;其他客户端,默认响应一个json数据。
区分浏览器和其他客户端的原理:
浏览器发送的请求头的Accept优先接收text/html,
而其他客户端发送的请求头的accept并没有指定
原理:ErrorMvcAutoConfiguration(错误处理的自动配置)
ErrorMvcAutoConfiguration 给容器中添加了以下组件:
- DefaultErrorAttributes 在页面共享信息
- BasicErrorController 处理/error请求
- ErrorPageCustomizer 定制错误的响应规则:系统出现错误来到error请求进行处理
- DefaultErrorViewResolver 解析去哪个错误页面
处理流程
一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error
请求;就会被BasicErrorController处理;
ErrorPageCustomizer
ErrorPageCustomizer 做的工作就是 来到 /error 请求
ErrorPageCustomizer 定制发生错误后的响应规则,
registerErrorPages方法中会通过getPath()得到 去的路径
getPath()的值为/error
BasicErrorController
BasicErrorController是用来处理/error请求的,分别针对不同的客户端,产生html类型的数据(errorHtml()方法) 、json数据(error()方法)
BasicErrorController处理/error请求:
响应页面errorHtml
通过resolveErrorView方法得到modelAndView,这个modelAndView就是我们返回的错误页面
resolveErrorView方法如下:
响应json数据error
DefaultErrorViewResolver
DefaultErrorViewResolver 解析去哪个错误页面,找错误页面的两个地方:
- 模板引擎
先去模板引擎找 - 静态资源
模板引擎没有就去静态资源下找
主要就是根据通过error/+错误的状态码 得到viewName 如果模板引擎可以解析这个页面地址就用模板引擎,不能就用resolveResource解析(即在静态资源文件夹下找)
resolveResource就是从静态资源中找
DefaultErrorAttributes
BasicErrorController中的errorHtml方法中,传入视图解析器的model 就是通过getErrorAttributes() 方法得到的
这个方法最终调用的是ErrorAttributes 的getErrorAttributes() 方法, 而DefaultErrorAttributes就是实现的ErrorAttributes接口
private final ErrorAttributes errorAttributes;
//---
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, ErrorAttributeOptions options) {
WebRequest webRequest = new ServletWebRequest(request);
return this.errorAttributes.getErrorAttributes(webRequest, options);
}
DefalutErrorAttributes的getErrorAttributes()方法如下:
通过这个方法我们我们就可以知道页面能获取的信息
定制错误响应
定制错误页面
根据DefaultErrorViewResolver 视图解析的过程(先再模板引擎中找error/状态码
,再到静态页面找),
- 有模板引擎
有模板引擎的情况下,我们就在在templates下写error/状态码.html
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态
码.html);
原因是DefaultErrorViewResolver中放了
有模板引擎页面能获取的信息
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里
原理:
BasicErrorController中的errorHtml方法中,传入视图解析器的model 就是通过getErrorAttributes() 方法得到的,getErrorAttributes() 最终就是调的DefalutErrorAttributes的getErrorAttributes()方法
- 没有模板引擎
没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找; - 以上都没,就默认
以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;
定制错误的json数据
浏览器和其他客户端都返回json
我们自己写一个MyExceptionHandler,定制我们想添加的数据后返回json数据
@ControllerAdvice
public class MyExceptionHandler {
//1. 浏览器和客户端都返回json
@ResponseBody
@ExceptionHandler(UserNotExistException.class) //处理UserNotExistException异常
public Map<String,Object> handleException(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("code","user.notexist");
map.put("message",e.getMessage());
return map;
}
SpringBoot2 默认对 exception 是关闭的,所以我们要在配置文件中改为true
server.error.include-exception=true
server.error.include-message=always
原理:
自适应 :浏览器返回页面,其他返回json
我们想要添加自己的数据后,还是让浏览器返回页面,其他返回json。 那就可以捕获异常后,通过请求转发/error 让BasicErrorController 处理。
我们要添加我们自己的数据,将我们的数据携带出去,就要写自己的MyErrorAttributes继承DefaultErrorAttributes
MyExceptionHandler 请求转发到/error
我们可以写一个MyExceptionHandler , 捕获异常后
-
将错误状态码改成4xx/5xx (因为BasicErrorController处理的时4xx或5xx错误,我们这种异常的状态码是200)
可以看到BasicErrorController的状态码是通过
request.getAttribute("javax.servlet.error.status_code")
得到的,因此我们可以如下设置状态码:request.setAttribute("javax.servlet.error.status_code",500);
-
添加我们的数据,放到request域中,让MyErrorAttributes可以获取到把这些数据传出去
map.put("code","user.notexist"); map.put("message",e.getMessage()); request.setAttribute("ext",map); //将我们的数据放到request域中
-
转发到/error 请求(因为BasicErrorController就是处理/error请求的)
MyErrorAttributes extends DefaultErrorAttributes 将我们定制的数据携带出去
出现错误以后,会来到/error请求,会被BasicErrorController处理。
而我们在MyExceptionHandler中添加的自己的数据,BasicErrorController并不知道,通过查看BasicErrorController源码,我们可以看到
响应出去可以获取的数据是由
getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);
页面上能用的数据,或者是json返回能用的数据都是通过DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的
因此我们可以通过继承DefaultErrorAttributes ,写一个我们的MyErrorAttributes 把我们的数据携带出去