zoukankan      html  css  js  c++  java
  • 【SpringBoot】SpringBoot 错误处理机制(九)

      本例介绍SpringBoot错误处理机制

    错误处理现象

      新建一个SpringBoot Web项目,在浏览器中随便输入一个错误地址进行访问,如:http://localhost:8081/test/aa,会出现一个错误页面

      浏览器页面错误:

        

      如果用PostMan请求错误地址,得到一个Json错误

      Json错误:

        

    错误处理原理

      可以参考ErrorMvcAutoConfiguration,错误处理的自动配置

      一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error请求;就会被BasicErrorController处理;

      1、DefaultErrorAttributes: 错误请求中有的属性

    1 @Override
    2 public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
    3     Map<String, Object> errorAttributes = new LinkedHashMap<>();
    4     errorAttributes.put("timestamp", new Date());
    5     addStatus(errorAttributes, webRequest);
    6     addErrorDetails(errorAttributes, webRequest, includeStackTrace);
    7     addPath(errorAttributes, webRequest);
    8     return errorAttributes;
    9 }

      2、BasicErrorController: 处理默认/error请求

     1 // 产生error界面
     2 @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
     3 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
     4     HttpStatus status = getStatus(request);
     5     Map<String, Object> model = Collections
     6             // getErrorAttributes 获取错误内容
     7             .unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
     8     response.setStatus(status.value());
     9 
    10     // 去哪个错误界面
    11     ModelAndView modelAndView = resolveErrorView(request, response, status, model);
    12     return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
    13 }
    14 
    15 // 产生错误json数据
    16 @RequestMapping
    17 public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    18     Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
    19     HttpStatus status = getStatus(request);
    20     return new ResponseEntity<>(body, status);
    21 }

      3、ErrorPageCustomizer: 系统出现错误以后来到error请求进行处理;(web.xml注册的错误页面规则)

     1 /**
     2  * {@link WebServerFactoryCustomizer} that configures the server's error pages.
     3  */
     4 private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
     5 
     6     private final ServerProperties properties;
     7 
     8     private final DispatcherServletPath dispatcherServletPath;
     9 
    10     protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {
    11         this.properties = properties;
    12         this.dispatcherServletPath = dispatcherServletPath;
    13     }
    14 
    15     // 注入错误页面
    16     @Override
    17     public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
    18         ErrorPage errorPage = new ErrorPage(
    19                 this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
    20         errorPageRegistry.addErrorPages(errorPage);
    21     }
    22 
    23     @Override
    24     public int getOrder() {
    25         return 0;
    26     }
    27 
    28 }

        // 错误路径配置

    1 @Value("${error.path:/error}")
    2 private String path = "/error"; 

      4、DefaultErrorViewResolver: 响应页面去哪个页面是由DefaultErrorViewResolver解析得到的;

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
    
            // status.series() 状态码
            modelAndView = resolve(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);
    
        if (provider != null) {
            //模板引擎可用的情况下返回到errorViewName指定的视图地址
            return new ModelAndView(errorViewName, model);
        }
        //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html
        return resolveResource(errorViewName, model);
    }

    定制错误响应

      1、定制错误的页面

        a、有模板引擎的情况下;error/状态码; 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的error文件夹下】,发生此状态码的错误就会来到 对应的页面; 可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);

          页面能获取的信息:

        • timestamp:时间戳
        • status:状态码
        • error:错误提示
        • exception:异常对象
        • message:异常消息
        • errors:JSR303数据校验的错误都在这里

          

          404.html如下:

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>404</title>
     6 </head>
     7 <body>
     8     <h3> 404 </h3>
     9     <h3> [[${status}]]</h3>
    10     <h3> [[${timestamp}]]</h3>
    11 </body>
    12 </html>
    View Code  

        b、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;

          

        c、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;

      2、定制错误的json数据

        a、自定义异常处理&返回定制json数据;没有自适应效果

     1 package com.test.springboot.controller;
     2 
     3 import com.test.springboot.exception.UserNotExistException;
     4 import org.springframework.web.bind.annotation.ControllerAdvice;
     5 import org.springframework.web.bind.annotation.ExceptionHandler;
     6 import org.springframework.web.bind.annotation.ResponseBody;
     7 
     8 import javax.servlet.http.HttpServletRequest;
     9 import java.util.HashMap;
    10 import java.util.Map;
    11 
    12 @ControllerAdvice
    13 public class MyExceptionHandler {
    14 
    15     // 浏览器和客户端返回的都是json
    16     @ResponseBody
    17     @ExceptionHandler(UserNotExistException.class)
    18     public Map<String, Object> handlerException(Exception e){
    19         HashMap<String, Object> map = new HashMap<>();
    20         map.put("code", "not.exist");
    21         map.put("message", e.getMessage());
    22         return map;
    23     }
    24 }

        b、转发到/error进行自适应响应效果处理

     1     // 页面请求 返回错误页面
     2     // 非页面请求 返回json数据
     3     @ExceptionHandler(UserNotExistException.class)
     4     public String handlerException(Exception e, HttpServletRequest request){
     5         // 设置自己的错误状态码 4xx 5xx
     6         // 否则就不会进入定制错误页面的解析流程
     7         /**
     8          * Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
     9          */
    10         request.setAttribute("javax.servlet.error.status_code", 500);
    11 
    12         HashMap<String, Object> map = new HashMap<>();
    13         map.put("code", "not.exist");
    14         map.put("message", e.getMessage());
    15         // 转发的error请求
    16         return "forward:/error";
    17     }

        c、将定制数据携带出去

          出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法); 

          方法1、完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中;

          方法2、页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到; 容器中DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的;

            方法2实现如下:

     1 // 给容器中添加自定义的ErrorAttributes
     2 @Component
     3 public class MyErrorAttributes extends DefaultErrorAttributes {
     4 
     5     // 返回值的map就是页面和json能获取的所有字段
     6     @Override
     7     public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
     8         Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
     9         // 加入公司字段
    10         map.put("company", "test.com");
    11         // 加入请求域request中的数据
    12         map.put("ext", webRequest.getAttribute("ext", 0));
    13 
    14         return map;
    15     }
    16 }

            效果如下(如果是页面,直接用${ext.code }获取即可):

            

  • 相关阅读:
    vuejs 踩坑及经验总结
    Factory Method
    【Java】macOS下编译JDK8
    康威定律(Conway's law)
    first-child和first-of-type
    JavaScript 核心学习——继承
    All Tips
    21分钟教会你分析MaxCompute账单
    CTO职场解惑指南系列(一)
    威胁预警|首现新型RDPMiner挖矿蠕虫 受害主机易被添加恶意账户
  • 原文地址:https://www.cnblogs.com/h--d/p/12387815.html
Copyright © 2011-2022 走看看