@RestControllerAdvice(@ControllerAdvice)拦截异常统一处理

@ControllerAdvice文档

spring 3.2中,新增了@ControllerAdvice 注解,用于定义@ExceptionHandler@InitBinder@ModelAttribute,并应用到所有@RequestMapping中。

如果全部异常处理返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody。

当不做任何处理时,默认的异常返回格式:

{
    "timestamp": "2021-07-28T08:30:03.641+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "message": "",
    "path": "/advice/test"
}

当我们希望返回跟接口正常返回的json数据相同的数据结构时,就需要用的@RestControllerAdvice 。

定义一个返回对象ResultVO

public class ResultVO {

    public int status;
    public String message;
    public Object data;
    
    public ResultVO(int status, String message) {
        this.status = status;
        this.message = message;
    }

    public ResultVO(Object data) {
        this.status = 200;
        this.message = "success";
        this.data = data;
    }
}

自定义异常处理类CustomerException

public class CustomerException extends RuntimeException{
    private String msg;
    private Integer code;

    public CustomerException( Integer code,String msg) {
        this.msg = msg;
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

自定义全局异常处理类GlobalExceptionHandler

@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局异常处理
     */
    @ExceptionHandler(Exception.class)
    public ResultVO exceptionListener(Exception e){
        return new ResultVO(500,e.toString());
    }

    /**
     *  自定义异常处理
     */
    @ExceptionHandler(CustomerException.class)
    public ResultVO exceptionListener(CustomerException e){
        return new ResultVO(e.getCode(),e.getMsg());
    }

    /**
     * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {}

    /**
     * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
     */
    @ModelAttribute
    public void addAttributes(Model model) {}

}

Controller中异常测试

    @RequestMapping("/test")
    public ResultVO testAdvice(){        
      throw new RuntimeException();
    }
    测试结果
    {
	    "status": 500,
	    "message": "java.lang.RuntimeException",
	    "data": null
	}
	
  	@RequestMapping("/test")
   	public ResultVO testAdvice(){ 
   		//可以定义一个异常枚举传参         
	    throw new CustomerException(400,"测试自定义异常");
   	}
	测试结果
	{
	    "status": 400,
	    "message": "测试自定义异常",
	    "data": null
	}

如果在controller层中只关心业务数据,而不去关心数据的形式。 就要通过实现ResponseBodyAdvice接口来统一处理报文。

将上面的GlobalExceptionHandler 重命名为GlobalResponseHandler实现 ResponseBodyAdvice接口

@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {

    /**
     * 全局异常处理
     */
    @ExceptionHandler(Exception.class)
    public ResultVO ExceptionListener(Exception e) {
        return new ResultVO(500, e.toString());
    }

    /**
     * 自定义异常处理
     */
    @ExceptionHandler(CustomerException.class)
    public ResultVO ExceptionListener(CustomerException e) {
        return new ResultVO(e.getCode(), e.getMsg());
    }

    /**
     * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
    }

    /**
     * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
     */
    @ModelAttribute
    public void addAttributes(Model model) {
    }

    
    //返回true执行beforeBodyWrite,返回false不执行
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        if (!(body instanceof ResultVO)) {
            return new ResultVO(body);
        }
        return body;
    }
}

当返回string类型的时候,HttpMessageConverter用的是StringHttpMessageConverter,这个时候就会报类型转换异常 ,ResultVO转换String失败。
解决方法:当返回string的时候,将RusultVO对象转换成json字符串返回

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        if (!(body instanceof ResultVO)) {
            if (body instanceof String) {
                return JSON.toJSONString(new ResultVO(body));
            }
            return new ResultVO(body);
        }
        return body;
    }

如果某个接口想返回其他类型的对象,就需要supports 方法返回false,此时可以定义一个注解@IgnoreSupport

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface IgnoreSupport {
}

在不需要封装返回数据的controller上使用@IgnoreSupport,supports方法中的MethodParameter 参数能获取到该注解即返回false,否则返回true。
修改后的supports方法:

 @Override
 public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
     return !methodParameter.hasMethodAnnotation(IgnoreSupport.class);
 }
    原文作者:不玩了把钱还我
    原文地址: https://blog.csdn.net/judicator0301/article/details/119182855
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞