一、默认效果
Spring Boot在默认情况下,对浏览器访问和其他客户端访问的处理机制是不同的。
1、浏览器访问
浏览器访问返回错误页面,错误页面中带有错误状态码,错误类型,提示信息等。
浏览器发送的请求头:
2、其他客户端访问
其他客户端访问,默认响应一个JSON数据,JSON数据中带有错误状态码,错误类型,提示信息等。
其他客户端发送的请求头
二、原理说明
Spring Boot的错误处理的自动配置参照ErrorMvcAutoConfiguration
,该自动配置类主要向容器中添加了以下四个组件。
一、DefaultErrorAttributes
/**
* 帮我们在页面共享信息,设置页面上的错误信息
**/
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, requestAttributes);
addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
addPath(errorAttributes, requestAttributes);
return errorAttributes;
}
二、BasicErrorController
/**
* 处理默认的/error请求
**/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
// 处理浏览发送的请求,产生html类型的数据
@RequestMapping(produces = "text/html")
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 ? new ModelAndView("error", model) : modelAndView);
}
// 处理其他客户端发送的请求,产生json数据
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(body, status);
}
三、ErrorPageCustomizer
// 系统出现错误以后来到error请求进行处理(web.xml注册的错误页面规则)
@Value("${error.path:/error}")
private String path = "/error";
四、DefaultErrorViewResolver
@Override
public ModelAndView resolveErrorView(HttpServletRequest request,
HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
//去error目录下找和状态码一致的错误页面 例如:404.ftl
String errorViewName = "error/" + viewName;
// 模板引擎可以解析这个页面地址就用模板引擎解析
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
if (provider != null) {
// 模板引擎可用的情况下返回到errorViewName指定的视图地址
return new ModelAndView(errorViewName, model);
}
// 模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面
return resolveResource(errorViewName, model);
}
一旦系统出现错误(例如:4xx/5xx),
ErrorPageCustomizer
就会生效,处理错误信息,然后来到/error
请求,被BasicErrorController
处理。
/**
* 具体响应页面由DefaultErrorViewResolver解析得到
**/
protected ModelAndView resolveErrorView(HttpServletRequest request,
HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
// 所有的ErrorViewResolver得到ModelAndView
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
Spring Boot错误页面默认放在资源路径的
/error
目录下,页面名称即错误代码(例如:404.ftl),你可以用“x”做错误页面模糊匹配(例如:4xx.ftl 匹配所有4开头的错误码)。同时存在时,精确优先。