• 简单认识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);
      }
      
  • 相关阅读:
    虚函数和纯虚函数
    MS CRM 2011中PartyList类型字段的实例化
    MS CRM 2011的自定义与开发(12)——表单脚本扩展开发(4)
    MS CRM 2011的自定义与开发(12)——表单脚本扩展开发(2)
    MS CRM 2011的自定义和开发(10)——CRM web服务介绍(第二部分)——IOrganizationService(二)
    MS CRM 2011 SDK 5.08已经发布
    MS CRM 2011 Q2的一些更新
    最近很忙
    Microsoft Dynamics CRM 2011最近的一些更新
    补一篇,Update Rollup 12 终于发布了
  • 原文地址:https://www.cnblogs.com/JIATCODE/p/13169989.html
Copyright © 2020-2023  润新知