ResponseBodyAdvice与@RestControllerAdvice

ResponseBodyAdvice与@RestControllerAdvice

image-20240802094142314

1. @RestControllerAdivce

@RestControllerAdvice是什么?

@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成,而@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。

@RestControllerAdvice的特点:

  • 通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。
  • 注解了@RestControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。
  • @RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。
  • @ExceptionHandler:用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。
  • @InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
  • @ModelAttribute:本来作用是绑定键值对到Model中,当与@ControllerAdvice配合使用时,可以让全局的@RequestMapping都能获得在此处设置的键值对

@ControllerAdvice可以指定Controller范围

  • 省略时,默认全局生效

  • basePackages:指定一个或多个包,这些包及其子包下的所有Controller都被该 @ControllerAdvice 管理。

  • basePackageClasses:是basePackages的一种变形,指定一个或多个Controller类,这些类所属的包及其子包下的所有Controller 都被该@ControllerAdvice管理。

  • assignableTypes:指定一个或多个Controller类,这些类被该@ControllerAdvice管理。

  • annotations:指定一个或多个注解,被这些注解所标记的Controller会被该@ControllerAdvice管理。

@RestControllerAdivce可以让我们处理异常时,返回结果按自己定义的类型进行返回。

一般用于全局异常的处理,如下

@RestControllerAdvice(basePackages = "cn.codewei.aopstudy.controller")
public class ControllerExceptionHandler {
    @ExceptionHandler(value = RuntimeException.class)
    public String runtimeExceptionHandler(RuntimeException e) {
        return "runtimeExceptionHandler: " + e.getMessage();
    }
}

2. ResponseBodyAdvice

ResponseBodyAdvice接口是在 Controller执行return之后,在response返回给客户端之前,执行的对response的一些处理,可以实现对response数据的一些统一封装或者加密等操作。

该接口一共有两个方法:

  • supports:判断是否要执行beforeBodyWrite方法,true为执行,false不执行。通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。
  • beforeBodyWrite:对 response 处理的具体执行方法。

如:

@RestControllerAdvice(basePackages = "cn.codewei.aopstudy.controller")
public class RewriteHandler implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 如果标记了RewriteResponse注解(自定义的注解),则通过(需要进行处理)
        boolean b = returnType.hasMethodAnnotation(RewriteResponse.class);
        if (b){
            return true;
        }
        return false;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        String s = String.valueOf(body);
        return s + " - RewriteHandler";  // 修改返回结果
    }
}

3. RequestBodyAdvice

在实际项目中,往往需要对请求参数做一些统一的操作,例如参数的过滤,字符的编码,第三方的解密等等,Spring提供了RequestBodyAdvice一个全局的解决方案,免去了我们在Controller处理的繁琐。

RequestBodyAdvice仅对使用了@RequestBody注解的生效 , 因为它原理上还是AOP,所以GET方法是不会操作的。

@ControllerAdvice(basePackages = "cn.codewei.controller")//此处设置需要当前Advice执行的域 , 省略默认全局生效
public class GlobalRequestBodyAdvice implements RequestBodyAdvice {
 
    /** 此处如果返回false , 则不执行当前Advice的业务 */
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
//        return methodParameter.getMethod().isAnnotationPresent(XXApiReq.class);
        return false;
    }
  
    /**
     * @title 读取参数前执行
     * @description 在此做些编码 / 解密 / 封装参数为对象的操作
     *
     *  */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        return new XHttpInputMessage(inputMessage, "UTF-8");
    }
 
    /**
     * @title 读取参数后执行
     * @author Xingbz
     */
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return inputMessage;
    }
 
    /**
     * @title 无请求体时的处理
     */
    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }
}
 
//这里实现了HttpInputMessage 封装一个自己的HttpInputMessage
class XHttpInputMessage implements HttpInputMessage {
    private HttpHeaders headers;
    private InputStream body;
 
    public XHttpInputMessage(HttpInputMessage httpInputMessage, String encode) throws IOException {
        this.headers = httpInputMessage.getHeaders();
        this.body = encode(httpInputMessage.getBody(), encode);
    }
 
    private InputStream encode(InputStream body, String encode) {
        //省略对流进行编码的操作
        return body;
    }
 
    @Override
    public InputStream getBody() {
        return body;
    }
 
    @Override
    public HttpHeaders getHeaders() {
        return null;
    }
}

Spring默认提供了接口的抽象实现类RequestBodyAdviceAdapter , 我们可以继承这个类按需实现 , 让代码更简洁一点

public abstract class RequestBodyAdviceAdapter implements RequestBodyAdvice {
 
	@Override
	public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType)
			throws IOException {
 
		return inputMessage;
	}
 
	@Override
	public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
 
		return body;
	}
 
	@Override
	@Nullable
	public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage,
			MethodParameter parameter, Type targetType,
			Class<? extends HttpMessageConverter<?>> converterType) {
 
		return body;
	}
}
posted @ 2025-03-27 20:38  mango0219  阅读(383)  评论(0)    收藏  举报