一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error请求;就会被BasicErrorController处理;
响应页面;去哪一个页面是由DefaultErrorViewResolver解析获得的;html
ErrorMvcAutoConfiguration ,此类帮咱们自动配置了:java
@Bean public ErrorPageCustomizer errorPageCustomizer() { return new ErrorPageCustomizer(this.serverProperties); } private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { /*添加错误映射,即发生错误时会发送 /error 请求*/ @Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { ErrorPage errorPage = new ErrorPage( this.properties.getServlet().getServletPrefix() + this.properties.getError().getPath()); errorPageRegistry.addErrorPages(errorPage); } } public class ErrorProperties { @Value("${error.path:/error}") private String path = "/error";
/*返回页面响应*/ @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); /*若是没有找到对应的 /error/5xx.html 视图 就会返回一个名为 error 的ModelAndView*/ return (modelAndView != null ? modelAndView : new ModelAndView("error", model)); } /*返回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); /*将响应状态码和响应数据封装成ResponseEntity 返回*/ return new ResponseEntity<>(body, status); }
protected HttpStatus getStatus(HttpServletRequest request) { /*从request 获取key为 javax.servlet.error.status_code 的值*/ Integer statusCode = (Integer) request .getAttribute("javax.servlet.error.status_code"); if (statusCode == null) { /*获取不到就响应一个500*/ return HttpStatus.INTERNAL_SERVER_ERROR; } try { return HttpStatus.valueOf(statusCode); } catch (Exception ex) { /*发生错误也响应一个500*/ return HttpStatus.INTERNAL_SERVER_ERROR; } }
public abstract class AbstractErrorController implements ErrorController { private final ErrorAttributes errorAttributes; protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) { WebRequest webRequest = new ServletWebRequest(request); /*就是调用ErrorAttributes 里的 getErrorAttributes 方法获取响应数据*/ return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace); } } public class ErrorMvcAutoConfiguration { /*为咱们注册了一个DefaultErrorAttributes */ @Bean @ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT) public DefaultErrorAttributes errorAttributes() { return new DefaultErrorAttributes( this.serverProperties.getError().isIncludeException()); } } //即调用了DefaultErrorAttributes 的 getErrorAttributes 方法,以下: @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap<>(); errorAttributes.put("timestamp", new Date()); addStatus(errorAttributes, webRequest); addErrorDetails(errorAttributes, webRequest, includeStackTrace); addPath(errorAttributes, webRequest); return errorAttributes; } /* 总的来讲就是放了以下数据 页面能获取的信息; timestamp:时间戳 status:状态码 error:错误提示 exception:异常对象 message:异常消息 errors:JSR303数据校验的错误都在这里 */
public class ErrorMvcAutoConfiguration { @Configuration @ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true) @Conditional(ErrorTemplateMissingCondition.class) protected static class WhitelabelErrorViewConfiguration { //这个即咱们常常看到的错误页 private final SpelView defaultErrorView = new SpelView( "<html><body><h1>Whitelabel Error Page</h1>" + "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>" + "<div id='created'>${timestamp}</div>" + "<div>There was an unexpected error (type=${error}, status=${status}).</div>" + "<div>${message}</div></body></html>"); // 便是这个View @Bean(name = "error") @ConditionalOnMissingBean(name = "error") public View defaultErrorView() { return this.defaultErrorView; } }
①,编写一个错误处理类,这里仍是用到了@ControllerAdvice 注解web
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; //声明是一个错误处理器 @ControllerAdvice public class ErrorControllAdvice { // 处理那种类型的错误 @ExceptionHandler(Exception.class) public String errorHandle(HttpServletRequest request){ // 添加额外错误响应数据 Map<String,Object> map=new HashMap<>(); // 添加一个公司的响应数据 map.put("company","小米"); // 放到request 中,而后在咱们定制的DefaultErrorAttributes 里获取 request.setAttribute("ext",map); // 设置响应状态码 request.setAttribute("javax.servlet.error.status_code",500); // 转发到/error 请求,这样就能根据客户端优化接收何种类型数据,决定响应html仍是json return "forward:/error"; } }
②,定制DefaultErrorAttributesspring
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.WebRequest; import java.util.Map; @Component public class MyDefaultErrorAttributes extends DefaultErrorAttributes { /*重写getErrorAttributes 方法*/ @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { // 获取原来的响应数据 Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace); //从请求域中拿值 Map<String, Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", RequestAttributes.SCOPE_REQUEST); //添加咱们定制的响应数据 map.put("ext",ext); // 返回带有咱们定制的数据的map return map; } }