zoukankan      html  css  js  c++  java
  • SpringBoot(六) -- SpringBoot错误处理机制

    一.SpringBoot中的默认的错误处理机制
      1.在SpringBootWeb开发中,当我们访问请求出现错误时,会返回一个默认的错误页面:

      2.在使用其他客户端访问的时候,则返回一个json数据:

      3.原理:可以参看原码ErrorMvcAutoConfiguration:
        (1)给容器中添加了以下组件
        DefaultErrorAttributes:帮我们在页面共享信息

     1     @Override
     2     public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
     3             boolean includeStackTrace) {
     4         Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
     5         errorAttributes.put("timestamp", new Date());
     6         addStatus(errorAttributes, requestAttributes);
     7         addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
     8         addPath(errorAttributes, requestAttributes);
     9         return errorAttributes;
    10     }
    11 
    12     private void addStatus(Map<String, Object> errorAttributes,
    13             RequestAttributes requestAttributes) {
    14         Integer status = getAttribute(requestAttributes,
    15                 "javax.servlet.error.status_code");
    16         if (status == null) {
    17             errorAttributes.put("status", 999);
    18             errorAttributes.put("error", "None");
    19             return;
    20         }
    21         errorAttributes.put("status", status);
    22         try {
    23             errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
    24         }
    25         catch (Exception ex) {
    26             // Unable to obtain a reason
    27             errorAttributes.put("error", "Http Status " + status);
    28         }
    29     }

        --可获取到的属性
          timestamp:时间戳
          status:状态码
          error:错误提示
          exception:异常
          message:异常消息
          errors:JSR303数据校验的错误都在这
        BasicErrorController:处理默认的/eerror请求

     1     @RequestMapping(produces = "text/html")
     2     public ModelAndView errorHtml(HttpServletRequest request,
     3             HttpServletResponse response) {
     4         HttpStatus status = getStatus(request);
     5         Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
     6                 request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
     7         response.setStatus(status.value());
     8         ModelAndView modelAndView = resolveErrorView(request, response, status, model);
     9         return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
    10     }
    11 
    12     @RequestMapping
    13     @ResponseBody
    14     public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    15         Map<String, Object> body = getErrorAttributes(request,
    16                 isIncludeStackTrace(request, MediaType.ALL));
    17         HttpStatus status = getStatus(request);
    18         return new ResponseEntity<Map<String, Object>>(body, status);
    19     }  

        --将会存在两种情况,得到一个html页面或者一个JSON数据,在浏览器发起的请求中,可发现其请求头accept中标识着优先接收html页面:

        --当其他客户端发送请求时,可以发现其请求头没有标注优先接收html数据(使用了*/*)

         --浏览器发送的请求将来到错误的html页面,而其他客户端则会接收到一个错误的JSON数据.在浏览器发送错误请求时,使用了ModelAndView声明了当前错误页面的地址和页面内容.

     1     protected ModelAndView resolveErrorView(HttpServletRequest request,
     2             HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
     3         for (ErrorViewResolver resolver : this.errorViewResolvers) {
     4             ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
     5             if (modelAndView != null) {
     6                 return modelAndView;
     7             }
     8         }
     9         return null;
    10     }

        --在该方法中将所有的ErrorViewResolver都获取到,而后返回,而我们去哪个页面则是由ErrorViewResolver得到的.
        ErrorPageCustomizer:系统出现错误时,来到error请求进行处理
        DefaultErrorViewResolver:

     1 @Override
     2     public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
     3             Map<String, Object> model) {
     4         ModelAndView modelAndView = resolve(String.valueOf(status), model);
     5         if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
     6             modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
     7         }
     8         return modelAndView;
     9     }
    10 
    11     private ModelAndView resolve(String viewName, Map<String, Object> model) {
    12         String errorViewName = "error/" + viewName;
    13         TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
    14                 .getProvider(errorViewName, this.applicationContext);
    15         if (provider != null) {
    16             return new ModelAndView(errorViewName, model);
    17         }
    18         return resolveResource(errorViewName, model);
    19     }

        --SpringBoot可以去找到某个页面,error/404,如果模板引擎可以解析这个地址,那么就是用模板引擎解析.如果模板引擎不可用,就在静态资源文件夹下寻找errorViewName对应的页面
        (2)一旦系统出现4XX或者5XX之类的错误,ErrorPageCustomizer就会生效(定制错误的响应规则),就会来到/error请求,就会被BasicErrorController进行处理

     

    二.如何定制错误响应
      1.如何定制错误的页面:在我们有模板引擎的情况下(error/404.html),将错误页面命名为错误状态码.html,放在模板引擎文件夹里面的error文件夹下,发生此状态码的错误就会来到对应的页面.如果我们为每一个状态码都配置一个错误页面的话,十分麻烦,我们可以给页面命名为4xx.html,这样当错误码不匹配的时候,将默认来到该html页面(优先寻找精确的状态码).
      当没有相对应的模板引擎时,将会在静态资源文件夹中寻找
      当模板引擎和静态资源的错误文件夹都没有的时候,将会来到SpringBoot的默认提示信息页面.
      2.如何定制错误的JSON数据

     1 package com.zhiyun.springboot.web_restfulcrud.exception;
     2 
     3 /**
     4  * @author : S K Y
     5  * @version :0.0.1
     6  */
     7 public class UserNotExistException extends RuntimeException {
     8     public UserNotExistException() {
     9         super("用户不存在");
    10     }
    11 }
    1     @ResponseBody
    2     @RequestMapping("/hello")
    3     public String hello(@RequestParam("user") String user) {
    4         if (user.equals("AAA")) {
    5             throw new UserNotExistException();
    6         }
    7         return "hello";
    8     }

     

    三.自定义JSON错误数据响应
      1.使用SpringMvc的异常处理机制(ExceptionHandler)捕获到这个异常之后,运行自定义的方法

     1 package com.zhiyun.springboot.web_restfulcrud.controller;
     2 
     3 import com.zhiyun.springboot.web_restfulcrud.entity.Employee;
     4 import com.zhiyun.springboot.web_restfulcrud.exception.UserNotExistException;
     5 import org.springframework.web.bind.annotation.ControllerAdvice;
     6 import org.springframework.web.bind.annotation.ExceptionHandler;
     7 import org.springframework.web.bind.annotation.ResponseBody;
     8 
     9 import java.util.HashMap;
    10 import java.util.Map;
    11 
    12 /**
    13  * @author : S K Y
    14  * @version :0.0.1
    15  */
    16 //在Spring中要成为一个异常处理器,需要使用该注解
    17 @ControllerAdvice
    18 public class MyExceptionHandler {
    19 
    20     @ResponseBody
    21     @ExceptionHandler(UserNotExistException.class)
    22     public Map<String, Object> handlerException(Exception e) {
    23         Map<String, Object> map = new HashMap<>();
    24         map.put("code", "user.not exist");
    25         map.put("message", e.getMessage());
    26         return map;
    27     }
    28 
    29 }

      2.到目前为止还没有实现我们的自适应效果(如果是浏览器访问则返回页面,其他客户端访问则是返回JSON数据):

    1     @ExceptionHandler(UserNotExistException.class)
    2     public String handlerException(Exception e) {
    3        /* Map<String, Object> map = new HashMap<>();
    4         map.put("code", "user.not exist");
    5         map.put("message", e.getMessage());*/
    6         //转发到error请求
    7         return "forward:/error";
    8     }

      3.按照上述的方法,可以实现自适应的页面响应,但是此时响应的是默认的页面 ,想要实现自适应的效果,需要传入我们自定义的状态码:

    1     @ExceptionHandler(UserNotExistException.class)
    2     public String handlerException(Exception e, HttpServletRequest request) {
    3         //传入我们自己的错误状态码
    4         request.setAttribute("javax.servlet.error.status_code",500);
    5         //转发到error请求
    6         return "forward:/error";
    7     }

      4.将我们的定制数据携带出去:
      (1)出现错误以后会来到error请求,这个请求会被BasicErrorController处理,他要返回的内容是由getErrorAttributes()方法得到的,而该方式是AbstractErrorController中随规定的方法,因此我们完全可以自定义一个Controller的实现类,或者是编写AbstractErrorController的子类来完成.
      (2)页面上能用的数据或者是JSON返回能用的数据都是通过errorAttributes.getErrorAttributes(requestAttributes,includeStackTrace);来得到的
      容器中DefaultErrorAttributes默认来进行数据处理,我们可以扩展该类来自定义返回信息:

     1 package com.zhiyun.springboot.web_restfulcrud.component;
     2 
     3 import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
     4 import org.springframework.web.context.request.RequestAttributes;
     5 
     6 import java.util.Map;
     7 
     8 /**
     9  * @author : S K Y
    10  * @version :0.0.1
    11  */
    12 public class MyErrorAttributes extends DefaultErrorAttributes {
    13     @Override
    14     public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
    15         Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
    16         map.put("company", "skykuqi");
    17         return map;
    18     }
    19 }

      (3)想要传递更多的信息,我们可以使用request来进行传递

     1     @ExceptionHandler(UserNotExistException.class)
     2     public String handlerException(Exception e, HttpServletRequest request) {
     3         //传入我们自己的错误状态码
     4         request.setAttribute("javax.servlet.error.status_code", 500);
     5         //传递错误信息
     6         Map<String, Object> map = new HashMap<>();
     7         map.put("code", "user.not exist");
     8         map.put("message", e.getMessage());
     9         request.setAttribute("errorMessage", map);
    10         //转发到error请求
    11         return "forward:/error";
    12     }
     1 package com.zhiyun.springboot.web_restfulcrud.component;
     2 
     3 import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
     4 import org.springframework.web.context.request.RequestAttributes;
     5 
     6 import java.util.Map;
     7 
     8 /**
     9  * @author : S K Y
    10  * @version :0.0.1
    11  */
    12 public class MyErrorAttributes extends DefaultErrorAttributes {
    13     @Override
    14     public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
    15         Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
    16         map.put("company", "skykuqi");
    17         //自定义异常处理器所携带的数据
    18         Map errorMap = (Map) requestAttributes.getAttribute("errorMessage", RequestAttributes.SCOPE_REQUEST);
    19         map.put("errorMessage", errorMap);
    20         return map;
    21     }
    22 }

      --最终的效果:我们可以定制ErrorAttributes来进行自定义错误信息的响应

  • 相关阅读:
    你不知道的JS
    Git学习攻略
    InteliJ Idea 编写项目常见的问题及解决方案
    上海租房攻略
    JS数组攻略
    Web开发必备的Chrome插件
    BFC和清除浮动
    Angular最新教程-第十一节 路由四 (嵌套路由)
    Angular最新教程-第十节 路由三(路由器钩子函数、路由守卫)
    Angular最新教程-第九节 路由二(路由策略、base标签、路由参数)
  • 原文地址:https://www.cnblogs.com/skykuqi/p/11985523.html
Copyright © 2011-2022 走看看