• 简单认识springboot的错误处理机制


    1.Spring Boot默认的错误处理机制

    默认的处理效果:

    1. 浏览器返回一个错误页面404
    2. 如果是其他客户端,默认响应一个json数据

    原理:ErrorMvcAutoConfiguration错误处理的自动配置

    给容器添加了一下组件:

    //ErrorAttributes,容器中没有这个组件则自动配置一个DefaultErrorAttributes,即我们看见的错误处理
    @Bean
    @ConditionalOnMissingBean(
        value = {ErrorAttributes.class},
        search = SearchStrategy.CURRENT
    )
    public DefaultErrorAttributes errorAttributes() {
        return new DefaultErrorAttributes();
    }
    //ErrorController
     @Bean
        @ConditionalOnMissingBean(
            value = {ErrorController.class},
            search = SearchStrategy.CURRENT
        )
        public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {
            return new BasicErrorController(errorAttributes, this.serverProperties.getError(), (List)errorViewResolvers.orderedStream().collect(Collectors.toList()));
        }
    //ErrorPageCustomizer
        @Bean
        public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
            return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
        }
    //DefaultErrorViewResolver
    @Bean
            @ConditionalOnBean({DispatcherServlet.class})
            @ConditionalOnMissingBean({ErrorViewResolver.class})
            DefaultErrorViewResolver conventionErrorViewResolver() {
                return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
            }
    

    步骤:

    ​ 一旦系统出现4xx或者5xx之类的错误,ErrorPageCustomizer就会生效(定制错误的响应规则),就会被BasicErrorController处理

    ​ 响应页面:去哪个页面是由DefaultErrorViewResolver解析得到的;通过DefaultErrorAttributes获得错误页面可共享的信息:

    ​ Status:状态码

    ​ exception:异常对象

    ​ message:异常消息

    ​ error :错误提示

    ​ timestamp:时间戳

    ​ errors:jsr303校验的错误

    源码如下:

    1. DefaultErrorAttributes

      //帮我们在页面共享信息
          public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
              Map<String, Object> errorAttributes = this.getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));   
              if (this.includeException != null) {
                  options = options.including(new Include[]{Include.EXCEPTION});
              }
              if (!options.isIncluded(Include.EXCEPTION)) {
                  errorAttributes.remove("exception");
              }
              if (!options.isIncluded(Include.STACK_TRACE)) {
                  errorAttributes.remove("trace");
              }
              if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
                  errorAttributes.put("message", "");
              }
              if (!options.isIncluded(Include.BINDING_ERRORS)) {
                  errorAttributes.remove("errors");
              }
              return errorAttributes;
          }
      
          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;
          }
      
    2. BasicErrorController

      @Controller
      @RequestMapping({"${server.error.path:${error.path:/error}}"})
      public class BasicErrorController extends AbstractErrorController {
      //响应ErrorPageCustomizer发出的错误请求,${server.error.path:${error.path:/error}}如果有配置文件中配置路径,也能获取
          
          
          //浏览器发送的请求请求头带有text/html,会来到这个方法处理
           @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.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
              response.setStatus(status.value());
              //ModelAndView  : 获得去往那个页面作为错误页面
              ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
              return modelAndView != null ? modelAndView : new ModelAndView("error", model);
          }
      
          //其他的设备请求头不会有text/html,就来到这个方法
          @RequestMapping   //ResponseEntity相应体力是map,list,javabean,输出格式也是json
          public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
              HttpStatus status = this.getStatus(request);
              if (status == HttpStatus.NO_CONTENT) {
                  return new ResponseEntity(status);
              } else {
                  Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
                  return new ResponseEntity(body, status);
              }
          }
      
    3. ErrorPageCustomizer

         @Value("${error.path:/error}")
          private String path = "/error";
      系统出现错误以后,来到error请求进行处理;
      
    4. DefaultErrorViewResolver

      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获取所有错误页面,取到对应的就返回
              ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
              modelAndView = resolver.resolveErrorView(request, status, model);
          } while(modelAndView == null);
      
          return modelAndView;
      }
      
      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;
      }
      
      private ModelAndView resolve(String viewName, Map<String, Object> model) {
          //默认SpringBoot可以去到一个页面?error/404
          String errorViewName = "error/" + viewName;
          
          //模板引擎可以解析这个页面地址就用模板引擎
          TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
          //模板引擎可用的情况下返回errorViewName指定的视图地址,不可用则在静态资源文件夹下找errorViewName页面
          //error/404.html
          return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
      }
      
  • 相关阅读:
    将HTTP请求对象转成curl命令行
    图片爬虫实践
    [CF1499E] Chaotic Merge
    [ICPC2020南京I] Interested in Skiing
    [ICPC2018西安J] Philosophical … Balance
    [ICPC2018西安L] Eventual … Journey
    [ICPC2018西安I] Misunderstood … Missing
    [ICPC2018西安D] Deja vu of … Go Players
    [ICPC2018西安F] Interstellar … Fantasy
    [ICPC2020南京L] Let's Play Curling
  • 原文地址:https://www.cnblogs.com/JIATCODE/p/13169989.html
Copyright © 2020-2023  润新知