• springboot错误处理机制及自定义错误处理


    springboot对于404,500等错误处理非常人性化,如果你是用浏览器访问的就返回一个页面,如果是客户端就会返回json数据,如下

    {
        "timestamp": "2020-03-13T08:24:41.493+0000",
        "status": 404,
        "error": "Not Found",
        "message": "No message available",
        "path": "/crud/aaa"
    }

    下面我们来简单的研究下原理,先看下错误处理器的自动配置类

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
    // 可以看出这个配置类是在webmvc自动配置之前加载的,这里主要加载了下面两个组件
    @AutoConfigureBefore(WebMvcAutoConfiguration.class)
    @EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })
    public class ErrorMvcAutoConfiguration {
    
      //将放回的错误信息封装进入默认的错误属性中
       @Bean
         @ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
        public DefaultErrorAttributes errorAttributes() {
            return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
        }
    
          //处理错误信息的controller
      @Bean
      @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
      public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,ObjectProvider<ErrorViewResolver> errorViewResolvers) {
         return new BasicErrorController(errorAttributes, this.serverProperties.getError(),errorViewResolvers.orderedStream().collect(Collectors.toList()));
      }
      
      //默认的错误页面定制器
      @Bean
      public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
      return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
      }
      //默认错误映射处理器
      @Configuration(proxyBeanMethods = false)
      static class DefaultErrorViewResolverConfiguration {

      private final ApplicationContext applicationContext;

       private final ResourceProperties resourceProperties;

      DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,ResourceProperties resourceProperties) {
      this.applicationContext = applicationContext;
      this.resourceProperties = resourceProperties;
       }

       @Bean
       @ConditionalOnBean(DispatcherServlet.class)
      @ConditionalOnMissingBean(ErrorViewResolver.class)
       DefaultErrorViewResolver conventionErrorViewResolver() {
      return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
      }

      }
    }

    而这个DefaultErrorAttributes类中,这个五个属性的添加正好和我们接收的json数据一一对应,错误信息就是封装到这里边的

    public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
    
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
            Map<String, Object> errorAttributes = new LinkedHashMap();
            errorAttributes.put("timestamp", new Date());//timestamp
            this.addStatus(errorAttributes, webRequest);//status
            this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);//error,message
            this.addPath(errorAttributes, webRequest);//path
            return errorAttributes;
        }
    }

    我们再来看下BasicErrorController这个基础错误处理器

    //处理所有的/error请求
    @Controller
    @RequestMapping("${server.error.path:${error.path:/error}}")
    public class BasicErrorController extends AbstractErrorController {
    //如果请求头接受方式为text/html,就是浏览器发送的,返回modelAndView就是默认的错误页面
    @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
        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);
        }
    
            //其余的请求方式呢,返回json数据
        @RequestMapping
        public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
            HttpStatus status = getStatus(request);
            if (status == HttpStatus.NO_CONTENT) {
                return new ResponseEntity<Map<String, Object>>(status);
            }
            Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
            return new ResponseEntity<>(body, status);
        }
    }

    那么每当我们的请求发生错误或者异常时,是如何重定向到/error呢?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);
        }
      private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //默认SpringBoot可以去找到一个页面? error/404
        String errorViewName = "error/" + viewName;
        //模板引擎可以解析这个页面地址就用模板引擎解析
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
        if (provider != null) {
          //模板引擎可用的情况下返回到errorViewName指定的视图地址
          return new ModelAndView(errorViewName, model);
        }
        //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html
        return resolveResource(errorViewName, model);
      }
    
    }
    没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;都没有返回默认错误页面

    既然知道了错误处理机制,实际开发中我们可以自己定制error返回自定义信息

    首先自定义异常处理器

    @ControllerAdvice
    public class MyExceptionHandler {
        //处理自定义的异常
        @ExceptionHandler(UserNotExistException.class)
        public String handleException(Exception e, HttpServletRequest request){
            //传入我们自己的错误状态码  4xx 5xx
            request.setAttribute("javax.servlet.error.status_code",599);
            Map<String,Object> map = new HashMap<>();
            map.put("code","user.notexist");
            map.put("message","用户出错啦");
            request.setAttribute("ext",map);
            //转发到/error
            return "forward:/error";
        }
    }

    但是此时DefaultErrorAttributes中只会封装默认的那五个值,所以我们需要自定义一个errorAttributes

    @Component
    public class MyErrorAttributes extends DefaultErrorAttributes {
    
        @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", 0);
            map.put("ext",ext);
            return map;
        }
    }

    至此就完成了自定义并且自适应的错误处理

  • 相关阅读:
    淘宝网的质量属性分析
    软件架构师如何工作
    软件需求管理用例方法三
    软件需求管理用例方法二
    git使用教程
    javascript获取鼠标点击位置的坐标兼容写法
    ES5中数组的方法
    JavaScript数组常用方法
    JavaScript中for..in循环陷阱介绍
    【转】web前端开发必知必会(面试、笔试可能用到)
  • 原文地址:https://www.cnblogs.com/vegeta-xiao/p/12487620.html
Copyright © 2020-2023  润新知