1、比如说: 我先注释掉拦截器,让问题重现一下:
这里我访问一个不存在的请求路径,就出现了错误(There was an unexpected error (type=Not Found, status=404).)。通过客户端 Postman 模拟请求一下(下图所示),默认响应一个 JSON 数据:
原理: 可以参照 ErrorMvcAutoConfiguration 进行错误处理的自动配置。 给容器中添加了以下组件:
1、DefaultErrorAttributes @Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
2、BasicErrorController:处理默认 /error 请求 @Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}
下面两个方法:第一个是产生 html 类型的数据(浏览器请求),第二个是产生 JSON 数据(客户端请求) @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 ? modelAndView : new ModelAndView("error", model));
}
@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<>(body, status);
}
区分是浏览器请求还是客户端请求: 浏览器:
客户端:
3、ErrorPageCustomizer @Bean
public ErrorPageCustomizer errorPageCustomizer() {
return new ErrorPageCustomizer(this.serverProperties);
}
4、DefaultErrorViewResolver @Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean
public DefaultErrorViewResolver conventionErrorViewResolver() {
return new DefaultErrorViewResolver(this.applicationContext, his.resourceProperties);
}
规则说明: 如果系统出现 4xx 或者 5xx 之类的错误,ErrorPageCustomizer 就会生效(定制错误的响应规则) @Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(
this.properties.getServlet().getServletPrefix()
this.properties.getError().getPath());
errorPageRegistry.addErrorPages(errorPage);
}
通过 this.properties.getError().getPath(), 得到 path 信息: /**
* Path of the error controller.
*/
@Value("${error.path:/error}")
private String path = "/error";
就会来到/error请求; 就会被 BasicErrorController 处理(参考 BasicErrorController 下的两段代码),去哪个页面显示。去哪个页面是由 DefaultErrorViewResolver 解析的: public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
private static final Map<Series, String> SERIES_VIEWS;
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);
}
@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) {
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);// 模板引擎可以解析这个页面地址就用模板引擎解析
if (provider != null) {//模板引擎可用的情况下返回到errorViewName指定的视图地址
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model);//模板引擎不可用,就在静态资源文件夹找errorViewName对应的页面 error/404.html,如果没有返回为null
}
|
发表评论 取消回复