SpringBoot 错误处理机制

SpringBoot 错误处理机制

SpringBoot默认的错误处理机制

  1. 浏览器:返回一个默认的错误页面

  2. 其他客户端,默认响应一个json数据

原理:

​ 参照ErrorMvcAutoConfiguration;错误处理的自动配置;

​ 给容器中添加了以下组件:

  • DefaultErrorAttributes:

    1. 在页面共享信息
  • BasicErrorController:

    @Controller
    @RequestMapping({"${server.error.path:${error.path:/error}}"})
    public class BasicErrorController extends AbstractErrorController {
    
    @RequestMapping(produces = {"text/html"})//产生html类型的数据
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
          HttpStatus status = this.getStatus(request);
          Map model =    Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
          response.setStatus(status.value());
    
          //去哪个页面作为错误页面;包含页面地址和页面内容
          ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
          return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
      }
    
      @RequestMapping
      @ResponseBody//产生json数据
      public ResponseEntity> error(HttpServletRequest request) {
          Map body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
          HttpStatus status = this.getStatus(request);
          return new ResponseEntity(body, status);
      }
  • ErrorPageCustomizer:

    public class ErrorProperties {
      @Value("${error.path:/error}")
      private String path = "/error"; //系统出现错误的时候来到error请求进行处理
  • DefaultErrorViewResolver:

    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map model) {
          ModelAndView modelAndView = this.resolve(String.valueOf(status), model);
          if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
              modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
          }
    
          return modelAndView;
      }
    
      private ModelAndView resolve(String viewName, Map model) {
          //默认SpringBoot可以去找一个页面? error/404
          String errorViewName = "error/" + viewName;
    
          //模板引擎可以解析这个页面地址就用模板引擎解析
          TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
          //模板引擎可用的情况下返回errorViewName指定的视图地址
          return provider != null ? new ModelAndView(errorViewName, model) : 
          //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面  error/404.html
          this.resolveResource(errorViewName, model);
      }

​ 步骤:

​ 一旦系统出现4xx或则5xx之类的错误;ErrorPageCustomizer就会生效,来到/error请求,就会被BasicErrorController处理;

定制错误响应

  1. 定制错误页面;

    • 有模板引擎的情况下; error/状态码

      ​ 将错误页面命名为”状态码“ 放在模板引擎文件夹下的error文件夹下,发生此状态码的错误就会来到对应的页面;

      可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html)

      ​ 页面可以获取的属性:

      ​ timestamp:时间戳

      ​ status:状态码

      ​ error:错误提示

      ​ exception:异常对象

      ​ message:异常消息

      ​ errors:JSR303数据校验的错误都在这里

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

  2. 定制错误的json数据

    • 自定义异常处理&返回定制json数据

      @ControllerAdvice
      public class MyExceptionHandler {
      @ResponseBody
       @ExceptionHandler(UserNotExistException.class)
       public Map handleException(Exception e) {
      
           Map map = new HashMap<>();
           map.put("code","user.notexist");
           map.put("message",e.getMessage());
           return map;
       }
      }
      //没有自适应效果
    • 转发到/error进行自适应响应效果处理

      @ExceptionHandler(UserNotExistException.class)
      public String handleException(Exception e, HttpServletRequest request) {
      
       /**
        * 需要传入我们自己的状态码 4xx 5xx 否则不会进入定制错误页面的解析流程
        * Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
         */
       request.setAttribute("javax.servlet.error.status_code",500);
      
       //转发到/error
       return "forward:/error";
      }
    • 将我们定制的数据携带出去;

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

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

      2、页面上能用个数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;

      ​ 容器中DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的

      自定义ErrorAttributes

      //给容器中加入我们自己定义的ErrorAttributes
      @Component
      public class MyErrorAttributes extends DefaultErrorAttributes {
      
       @Override
       public Map getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
           Map map = super.getErrorAttributes(requestAttributes, includeStackTrace);
      
           //我们的异常处理器携带的数据
           Map ext = (Map) requestAttributes.getAttribute("ext", 0);
           map.put("company","day1");
           map.put("ext",ext);
           return map;
       }
      } 

您可能还喜欢...

发表评论

电子邮件地址不会被公开。 必填项已用*标注