zoukankan      html  css  js  c++  java
  • 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;
        }
    }

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

  • 相关阅读:
    MySql cmd下的学习笔记 —— 引擎和事务(engine,transaction)
    MySql cmd下的学习笔记 —— 有关视图的操作(algorithm)
    MySql cmd下的学习笔记 —— 有关视图的操作(建立表)
    MySql cmd下的学习笔记 —— 有关常用函数的介绍(数学函数,聚合函数等等)
    MySql cmd下的学习笔记 —— 有关多表查询的操作(多表查询练习题及union操作)
    MySql 在cmd下的学习笔记 —— 有关多表查询的操作(内连接,外连接,交叉连接)
    MySql cmd下的学习笔记 —— 有关子查询的操作(where型,from型,exists型子查询)
    MySql cmd下的学习笔记 —— 有关select的操作(order by,limit)
    剑指Offer--第21题 调整数组顺序使奇数位于偶数前面;
    剑指Offer--和为s的连续正数序列
  • 原文地址:https://www.cnblogs.com/vegeta-xiao/p/12487620.html
Copyright © 2011-2022 走看看