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 }获取即可):

            

  • 相关阅读:
    用 Python 带你看各国 GDP 变迁
    Fluent Interface(流式接口)
    probing privatePath如何作用于ASP.NET MVC View
    Word插入htm文件导致文本域动态增加的一个问题
    Visual Studio 2013附加进程调试IE加载的ActiveX Control无效解决方法
    Ubuntu下Chrome运行Silverlight程序
    Windows Phone Bing lock screen doesn't change解决方法
    SPClaimsUtility.AuthenticateFormsUser的证书验证问题
    Web Service Client使用Microsoft WSE 2.0
    Visual Studio 2013安装Update 3启动crash的解决方法
  • 原文地址:https://www.cnblogs.com/h--d/p/12387815.html
Copyright © 2011-2022 走看看