zoukankan      html  css  js  c++  java
  • SpringBoot系列: Spring支持的异常处理方式

    ===================================
    视图函数返回 status code 的方式
    ===================================
    Spring 有一个专门的枚举类型 HttpStatus, 比如 HttpStatus.NOT_FOUND
    1. 视图函数返回 ResponseEntity 类型的对象.
    2. 在 exception 类加注解 @ResponseStatus, 一旦视图函数抛出这个异常, Spring 就会自动返回设定的 status code.
    3. 在视图函数上加 @ResponseStatus, 只要该函数没有报异常, Spring 就会自动返回设定的 status code.

    ===================================

    方式一: 在业务 Controller 内部增加异常视图函数
    ===================================
    在我们的 Controller 类中专门加一个或几个处理异常的视图函数, 这些异常处理视图需要加 @ExceptionHandler 注解.

    将 @ExceptionHandler 注解的加到异常视图函数上, 也可以将 @ResponseStatus 注解一起加上. 异常视图函数的参数和普通视图函数的参数不一样, 能传入 Exception/HttpServletRequest/HttpServletResponse/HttpSession/Principle 等参数, 但是不能传入 Model 之类的参数, 视图方法的返回类型可以是: void/String 或 ModelAndView.

    该方式缺点是: 这种异常处理仅仅在本 controller 有效, 不太好做到通用的异常处理. 当然也可设计一个通用的 Controller base 类, 在 base 类中加上异常处理视图函数, 所有业务 controller 都继承这个基类.


    ===================================
    方式二: 实现一个基于 HandlerExceptionResolver 接口的 bean
    ===================================
    在 Spring Web 项目中, 如果我们的 bean 实现了 HandlerExceptionResolver 接口, 一旦某个 controller 抛出异常, 这个 bean 会截获并处理异常.
    该方法的缺点是: 不太好返回具体的报错内容.

    一般情况下, 我们的 bean 并不直接实现 HandlerExceptionResolver 接口, 而是继承 SimpleMappingExceptionResolver 类.

    通常做法是, 我们先自定义一个 SimpleMappingExceptionResolver 子类, 实现 doResolveException() 方法, 然后在 Mvc Configruation 配置类中, 声明一个名为 simpleMappingExceptionResolver 的 bean. 之所以继承 SimpleMappingExceptionResolver 类, 是因为它已经实现了很多有用的功能, 我们直接使用即可, 比如:
    1. 设定 exception 和 view 视图的 mapping 关系等
    2. 设置缺省的异常处理视图名
    3. 异常 log 功能 (需启用)

    除了 SimpleMappingExceptionResolver 类之外, Spring 还有其他几个 HandlerExceptionResolver 实现类, 作用分别是:
    1. ExceptionHandlerExceptionResolver, 用来截获标注 @ExceptionHandler 注解的视图函数异常, @ExceptionHandler 加在专门的异常处理视图函数上.
    2. ResponseStatusExceptionResolver, 用来截获和 @ResponseStatus 注解相关的异常, @ResponseStatus 可以加在 Exception 类或视图函数上.
    3. DefaultHandlerExceptionResolver, 用来截获标准的 Spring exception, 并将异常转成相应 Http status code, 比如 404 等.


    ===================================
    方式三: 使用 @ControllerAdvice 注解声明一个异常处理类
    ===================================
    @ControllerAdvice 是 Spring 3.2 之后引入的. 在一个业务 controller 抛出异常后, @ControllerAdvice 标注的类会截获该异常. @ControllerAdvice 还有一个兄弟 @RestControllerAdvice 注解.
    @ControllerAdvice 标注在类上, 具体异常视图函数仍然通过加 @ExceptionHandler 注解声明的, 该视图函数能传入 Exception/HttpServletRequest/HttpServletResponse/HttpSession/Principle 等参数, 但是不能传入 Model 之类的参数, 视图方法的返回类型可以是: void/String 或 ModelAndView.

    @ControllerAdvice 优点有:
    1. @ControllerAdvice 处理机制能接管所有 controller 类的异常, 更容易实现全局统一的异常处理.
    2. 可以自由组合不同的 Exception 的异常处理视图.
    3. 异常视图函数可以用 @ResponseStatus 注解指定返回码. 视图函数返回类型可以是 void/String 或 ModelAndView, 所以很容易返回报错内容.
    4. 我们 @ControllerAdvice 注解类最好继承至 ResponseEntityExceptionHandler, 该 ResponseEntityExceptionHandler 类已经内置了很多标准异常的异常处理视图, 所以我们不需要再关心标准异常的处理, 只需要关心和业务相关的异常即可.

    ===================================
    Spring Boot 默认的出错处理
    ===================================
    Spring MVC 没有提供缺省的 fall-back 出错页, 而 Spring Boot 默认情况下就有很完善的报错处理机制, 一旦视图函数报错, Spring boot 先看有没有映射 /error 路径的视图, 有的话渲染该视图, 没有的话, 会展现 "Whitelabel Error Page", 该页面能显示出 Status code 和详细的报错信息. 如果是 Restful 请求的话, Whitelabel 报错也是 json 格式的反馈.

    比如: 

    $> curl -H "Accept: application/json" http://localhost:8080/no-such-page

    返回是: 

    {"timestamp":"2018-04-11T05:56:03.845+0000","status":404,"error":"Not Found","message":"No message available","path":"/no-such-page"}

    默认缺省的异常处理路径是 /error,  也可以通过 server.error.path 属性修改. 

    Spring Boot 之所以有一个 whitelabel error页面, 是因为 Spring Boot 会注入一个 BasicErrorController 用来处理异常, 相关的 application 属性有: 

    server.error.include-exception=false # Include the "exception" attribute.
    server.error.include-stacktrace=never # When to include a "stacktrace" attribute.
    server.error.path=/error # Path of the error controller.
    server.error.whitelabel.enabled=true # Whether to enable the default error page displayed in browsers in case of a server error.

    如果缺省的 whitelabel 页面不能满足需求, 可以简单定制 page 页, 在 resources 目录下, 新建一个 error.html 模板文件, 在文件中, 可以使用下面的变量.
    ${timestamp} 、 ${path} 、 ${error} 、 ${status} 、 ${message} 、 ${exception}、 ${trace}

    比如要对 404 访问做日志,  我们可以仿造 BasicErrorController 写一个自己的 ErrorController 类. Spring boot 会自动这个 ErrorController 实例代替缺省的 ErrorController.

    ===================================
    推荐的做法:
    ===================================
    1. 在一个项目中, 仅使用一种异常处理方法, 不要组合使用, 因为有可能和我们的预期不一致.
    2. 推荐使用全局性的异常处理, 而不是 Controller 级别的异常处理; 推荐 @ControllerAdvice, 而不是基于 HandlerExceptionResolver 接口的 bean.
    3. @ControllerAdvice 注解类最好继承至 ResponseEntityExceptionHandler, 该 ResponseEntityExceptionHandler 类已经内置了很多标准异常的异常处理视图, 所以我们不需要再关心标准异常的处理, 只需要关心和业务相关的异常即可.

    需要说明的是, 只有进入 Controller 的异常, @ControllerAdvice 才能捕获, 对于拦截器的异常和 url写错这类404 这样的异常, 是捕获不到的, 当然一般情况下我们也没有必要处理这些异常, 如果一定要对 404 访问做日志,  我们可以仿造 BasicErrorController 写一个自己的 ErrorController 类.

    下面是一个简单的示意代码: 

    @ControllerAdvice 
    class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    
        // 针对不同的异常, 定义其处理视图函数
        // some view functions
            
            
        // 对应 uncaughted 异常, 使用下面这个视图函数来兜底
        public static final String DEFAULT_ERROR_VIEW = "error";
        @ExceptionHandler(value = Exception.class)
        public ModelAndView defaultErrorHandler(HttpServletRequest req, HttpServletResponse rep, Exception e) throws Exception {
           //针对 500 错误, 需要记录 error 日志. 
           logger.error(e.getMessage(),e);
           if (req.getHeader("Accept").contains("application/json"))
           { 
               //write json to HttpServletResponse
               return null;
           }
           else{
                ModelAndView mav = new ModelAndView();
                mav.addObject("exception", e);
                mav.addObject("url", req.getRequestURL());
                mav.setViewName(DEFAULT_ERROR_VIEW);
                return mav;
            }
        }    
    }

    ===================================
    参考:
    ===================================
    https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
    https://blog.csdn.net/aiyaya_/article/details/78725755
    http://blog.didispace.com/springbootexception/
    https://blog.csdn.net/chinrui/article/details/71036544
    http://tengj.top/2018/05/16/springboot13/

  • 相关阅读:
    手机网页中 唤醒拨打电话和发送短信,浏览器和微信浏览器都可以用
    用于滚动 焦点图的MSClass
    oracle使用connect by进行级联查询 树型菜单
    Jstl标签汇总
    EL表达式报错:  According to TLD or attribute directive in tag file, attribute value does not accept any expressions
    EL表达式简介
    SSM整合(四)-整合后配置文件汇总
    Maven的pom报maven-surefire-plugin:pom:2.12.4
    SSM整合(三):Spring4与Mybatis3与SpringMVC整合
    SSM整合(二):Spring4与Mybatis3整合
  • 原文地址:https://www.cnblogs.com/harrychinese/p/SpringBoot_exception_handler.html
Copyright © 2011-2022 走看看