错误处理机制

SpringBoot 默认的错误处理机制
默认效果:
1. 浏览器 返回默认页面
SpringBoot自学好几天 中途开始写笔记 SpringBoot Web开发 错误处理原理  定制错误页面  20190128_SpringBoot
浏览器发送请求的请求头:
SpringBoot自学好几天 中途开始写笔记 SpringBoot Web开发 错误处理原理  定制错误页面  20190128_SpringBoot_02
2. 其他客户端访问 默认响应一个json数据

SpringBoot自学好几天 中途开始写笔记 SpringBoot Web开发 错误处理原理  定制错误页面  20190128_SpringBoot_03
客户端发送请求的请求头是:
SpringBoot自学好几天 中途开始写笔记 SpringBoot Web开发 错误处理原理  定制错误页面  20190128_SpringBoot_04
原理:
参照 ErrorMvcAutoConfiguration ;错误处理的自动配置
给容器中添加了以下组件:
1. DefaultErrorAttributes

//帮我们在页面共享信息 public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {Map<String, Object> errorAttributes = new LinkedHashMap();errorAttributes.put("timestamp", new Date());this.addStatus(errorAttributes, webRequest);this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);this.addPath(errorAttributes, webRequest);return errorAttributes;}

3. BasicErrorController :处理默认 /error请求

//@Controller@RequestMapping({"${server.error.path:${error.path:/error}}"})public class BasicErrorController extends AbstractErrorController {

   @RequestMapping(produces = {"text/html"} )//产生html格式数据  浏览器发生的请求public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = this.getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));response.setStatus(status.value());//去哪个页面作为错误页面 ; 包含页面地址和页面内容ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);return modelAndView != null ? modelAndView : new ModelAndView("error", model);}@RequestMapping//产生json数据  其他客户端public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));HttpStatus status = this.getStatus(request);return new ResponseEntity(body, status);}

3. ErrorPageCustomizer
4. DefaultErrorViewResolver

 	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);}return modelAndView;}//默认SpringBoot可以去找到一个页面  error/状态码(404 405 等 )private ModelAndView resolve(String viewName, Map<String, Object> model) {String errorViewName = "error/" + viewName;//如果模板引擎能解析这个页面地址 那就用模板引擎解析TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);//模板引擎可用的情况下 返回视图  模板引擎不可用调用resolveResourcereturn provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);}//模板引擎不可用 就在静态资源文件夹下找 viewName 对应的页面 error/状态码.html
   private ModelAndView resolveResource(String viewName, Map<String, Object> model) {String[] var3 = this.resourceProperties.getStaticLocations();int var4 = var3.length;for(int var5 = 0; var5 < var4; ++var5) {String location = var3[var5];try {Resource resource = this.applicationContext.getResource(location);resource = resource.createRelative(viewName + ".html");if (resource.exists()) {return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);}} catch (Exception var8) {;}}return null;}

@Value("${error.path:/error}")private String path = "/error";//系统出现错误以后 到error请求进行处理(类似于之前在web.xml中注册的一些错误对应的规则)

  1. 等等
    步骤: 一旦系统出现4xx或5xx之类的错误 ;ErrorPageCustomizer就会生效 (定制错误响应规则 );就会被BasicErrorController 处理
    1 ) 响应页面. 去哪个页面 是由defaultErrorView处理的 (上边的4)
    protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {Iterator var5 = this.errorViewResolvers.iterator();ModelAndView modelAndView;do {if (!var5.hasNext()) {return null;}ErrorViewResolver resolver = (ErrorViewResolver)var5.next();modelAndView = resolver.resolveErrorView(request, status, model);} while(modelAndView == null);return modelAndView;}

2 )
3. 如何定制错误响应页面

  • 定制错误页面
    1)有模板引擎的情况下
    视图地址:error/状态码 【将错误页面 命名为错误状态码.html 放在模板引擎文件夹里边的 error 文件夹下】
    所有以4开头的错误 都可以写一个页面叫4xx.html
    所有以4开头的错误 都可以写一个页面叫5xx.html
    精确优先 也就是如果有404.html 500.html 就不会用4xx.html 5xx.html
    页面能获取的信息:
    timestamp
    status:状态码
    error:错误提示
    exception:异常对象
    message:异常消息
    errors:JSR303 数据校验的错误
    2)没有模板引擎(模板引擎找不到错误页面)
    也可以放在静态资源文件夹下 但是没法获取页面信息

    3)模板引擎和静态资源文件夹下都没有
    以上都没有 默认到SpringBoot的默认错误提示页面

  • 定制错误json数据
    4 . 如何定制错误的JSON数据

  1. 自定义异常处理&返回定制json数据
/**
 * @author LM
 * @create 2019-01-28 22:34
 */@ControllerAdvicepublic class MyExceptionHandler {@ResponseBody@ExceptionHandler({UserNotExits.class}) //Exception.classpublic Map<String,Object> handleException(Exception e){Map<String,Object> map = new HashMap<>();map.put("code","user.notexit");map.put("message",e.getMessage());return map;}}//没有自适应效果  (浏览器访问 返回页面     其他客户端访问返回json)//现在浏览器客户端返回的都是json

  1. 转发到/error进行自适应响应 效果处理
  @ExceptionHandler({UserNotExits.class}) //Exception.classpublic String handleException(Exception e){Map<String,Object> map = new HashMap<>();map.put("code","user.notexit");map.put("message",e.getMessage());//转发到/errorreturn "forward:/error";}
   //已经自适应了  但是页面映射还是有问题的哦

    @ExceptionHandler({UserNotExits.class}) //Exception.classpublic String handleException(Exception e, HttpServletRequest request){//传入自己错误状态码 4xx  5xx   否则就不会进入错误页面 //      Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");request.setAttribute("javax.servlet.error.status_code","400");Map<String,Object> map = new HashMap<>();map.put("code","user.notexit");map.put("message",e.getMessage());//转发到/errorreturn "forward:/error";}

自己定义的code什么的都没有了!!
SpringBoot自学好几天 中途开始写笔记 SpringBoot Web开发 错误处理原理  定制错误页面  20190128_SpringBoot_05
3)将我们的定制数据 携带进去;
出错以后,会来到/error请求 这个请求会被BaseErrorController 处理
BaseErrorController做了自适应(返回页面 返回json数据 ),响应出去的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)的方法);

  1. 完全来编写一个ErrorController的实现类[或者编写AbstractErrorController的实现类] ,放在容器中;
  2. 页面上能用的数据 或者json返回能用的数据
    都是通过this.errorAttributes.getErrorAttributes()【AbstractErrorController】得到的都是通过
    容器中DefaultErrorAttributes.getErrorAttributes() 默认进行处理的
    自定义
/**
 * @author LM  加入自己定义的ErrorAttributes
 * @create 2019-01-28 23:13
 */@Componentpublic class MyErrorAttributes extends DefaultErrorAttributes {@Overridepublic Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {Map<String, Object> map =  super.getErrorAttributes(webRequest, includeStackTrace);map.put("company","家里蹲");return map;}}

上边异常中自定义的信息没有放进去呢:
1)放 request中
SpringBoot自学好几天 中途开始写笔记 SpringBoot Web开发 错误处理原理  定制错误页面  20190128_SpringBoot_06
2)

/**
 * @author LM  加入自己定义的ErrorAttributes
 * @create 2019-01-28 23:13
 */@Componentpublic class MyErrorAttributes extends DefaultErrorAttributes {@Overridepublic Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {Map<String, Object> map =  super.getErrorAttributes(webRequest, includeStackTrace);map.put("company","家里蹲");//我们的异常处理器 携带的数据Map<String, Object> ext  = (Map<String, Object>)webRequest.getAttribute("ext",0);map.put("ext",ext);return map;}}

SpringBoot自学好几天 中途开始写笔记 SpringBoot Web开发 错误处理原理  定制错误页面  20190128_SpringBoot_07