zoukankan      html  css  js  c++  java
  • springboot 详解RestControllerAdvice(ControllerAdvice)(转)

    springboot 详解RestControllerAdvice(ControllerAdvice)拦截异常并统一处理
    简介

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface ControllerAdvice {
    @AliasFor("basePackages")
    String[] value() default {};
    
    @AliasFor("value")
    String[] basePackages() default {};
    
    Class<?>[] basePackageClasses() default {};
    
    Class<?>[] assignableTypes() default {};
    
    Class<? extends Annotation>[] annotations() default {};
    }

    作为特化@Component,允许通过类路径扫描自动检测实现类。

    它通常用于定义@ExceptionHandler, @InitBinder 和 @ModelAttribute 适用于所有@RequestMapping方法的方法。

    annotations(),basePackageClasses(), basePackages()或它的别名value() 可以被指定,以限定控制器,以协助的特定子集。当应用多个选择器时,应用OR逻辑 - 意味着所选的控制器应匹配至少一个选择器。

    默认行为(即,如果没有任何选择器使用),带@ControllerAdvice注释的类将协助所有已知的控制器。

    背景
    当我们定义了一个自定义返回参数格式时,希望得到统一的返回,如果在运行时发现了异常,也希望将异常统一返回。如
    期望返回格式:

    {
    "msg": "success",
    "code": 500,
    "success": false,
    "message": "id不能为空!"
    }

    抛出异常格式:

    {
    "timestamp": "2019-04-01T07:17:38.619+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "id不能为空!",
    "path": "/api/myInfo"
    }

    如何让我们的异常得到期望的返回格式,这里就需要用到了@ControllerAdvice或者RestControllerAdvice(如果全部异常处理返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody。)。下面看一个demo。

    准备工作
    1.创建一个UnionExceptionHandler类,定义全局异常捕捉处理。

    package com.honghh.bootfirst.exception;
    
    import com.honghh.bootfirst.utils.R;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.InitBinder;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    /**
    * ClassName: UnionExceptionHandler
    * Description:
    *
    * @author honghh
    * @date 2019/04/01 10:03
    */
    @Slf4j
    @RestControllerAdvice
    public class UnionExceptionHandler {
    
    /**
    * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
    *
    * @param binder
    */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
    log.info("binder.getFieldDefaultPrefix {}",binder.getFieldDefaultPrefix());
    log.info("binder.getFieldMarkerPrefix {}",binder.getFieldMarkerPrefix());
    }
    /**
    * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
    * @param model
    */
    @ModelAttribute
    public void addAttributes(Model model) {
    model.addAttribute("author", "harry");
    }
    /**
    * Description : 全局异常捕捉处理
    * Group :
    *
    * @author honghh
    * @date 2019/4/1 0001 10:34
    * @param ex
    * @return
    */
    @ExceptionHandler(RRException.class)
    public R apiExceptionHandler(RRException ex) {
    log.error("ApiException 异常抛出:{}", ex);
    return R.fail(ex);
    }
    
    /**
    * Description : 针对某个异常捕捉处理
    * Group :
    *
    * @author honghh
    * @date 2019/4/1 0001 10:34
    * @param ex
    * @return
    */
    @ExceptionHandler(SSException.class)
    public R apiExceptionHandler(SSException ex) {
    log.error("ApiException 异常抛出:{}", ex);
    return R.fail(ex);
    }
    }


    启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 @RequestMapping 注解的方法上。
    @ModelAttribute:在Model上设置的值,对于所有被 @RequestMapping 注解的方法中,都可以通过 ModelMap 获取
    2.创建一个RRException 自定义异常。
    package com.honghh.bootfirst.exception;

    /**
    * 自定义异常
    *
    * @author harry
    * @date 2018-07-20 16:30
    */
    public class RRException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    
    private String msg;
    private int code = 500;
    
    public RRException(String msg) {
    super(msg);
    this.msg = msg;
    }
    
    public RRException(String msg, Throwable e) {
    super(msg, e);
    this.msg = msg;
    }
    
    public RRException(String msg, int code) {
    super(msg);
    this.msg = msg;
    this.code = code;
    }
    
    public RRException(String msg, int code, Throwable e) {
    super(msg, e);
    this.msg = msg;
    this.code = code;
    }
    
    public String getMsg() {
    return msg;
    }
    
    public void setMsg(String msg) {
    this.msg = msg;
    }
    
    public int getCode() {
    return code;
    }
    
    public void setCode(int code) {
    this.code = code;
    }
    
    
    }

    3.写一个RequestMapping方法,抛出异常进行测试。[spring boot 默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好,下面自定义异常处理,提供友好展示。

    @GetMapping("myInfo")
    public R myInfo(@RequestParam Integer id) {
    if (id == null) {
    throw new RRException("id不能为空!");
    }
    MyInfo myInfo = myInfoService.getById(id);
    return R.ok().put("myInfo", myInfo);
    }
    
    //启动应用,访问:http://127.0.0.1:8080/api/myInfo?id= ,正常显示以下json内容,证明自定义异常已经成功被拦截。
    
    {
    "msg": "success",
    "code": 500,
    "success": false,
    "message": "id不能为空!"
    }

    代码获取
    https://gitee.com/honghh/boot-demo.git

    参考文献
    https://docs.spring.io/spring-framework/docs/5.0.0.M1/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html
    ————————————————
    版权声明:本文为CSDN博主「AH_HH」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_35098526/article/details/88949425

  • 相关阅读:
    面试题汇总
    桥接模式
    2010412 面试题
    2010412 面试题1
    访问者模式
    原码、反码、补码什么意思?有什么用?
    装饰模式
    mysql 忘记root密码 进行重置
    运维开源工具一览
    编写一个函数计算小费,小费为总账单的20%
  • 原文地址:https://www.cnblogs.com/muxi0407/p/11950475.html
Copyright © 2011-2022 走看看