文章目录
简介
ResponseBodyAdvice是Spring框架中的一个接口,用于在Controller方法返回结果之前对响应体进行处理。它可以对响应体进行加密、压缩、格式化等操作,从而优化程序的响应效率和用户体验。
RequestBodyAdvice和ResponseBodyAdvice增强器:点击跳转
spring的RequestBodyAdvice和ResponseBodyAdvice增强器
适用于路由器的请求和响应处理,比如验签,加解密,压缩解压,脱敏,格式化。
ResponseBodyAdvice实践
实现ResponseBodyAdvice接口
supports断言beforeBodyWrite是否执行。
@Slf4j
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
/**
* 如果返回true,则beforeBodyWrite方法被执行
*
* @param returnType the return type
* @param converterType the selected converter type
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
log.info("returnType:{},converterType:{}", returnType, converterType);
if (Objects.equals(converterType, MappingJackson2HttpMessageConverter.class)) {
log.info("消息类型正确");
Method method = returnType.getMethod();
assert method != null;
ChangeBody annotation = method.getAnnotation(ChangeBody.class);
//存在注解的就进行解析
if (Objects.nonNull(annotation)) {
log.info("注解正确");
return true;
}
}
return false;
}
/**
* 作用在消息体HttpMessageConverter之后,body写操作之前
*
* @param body the body to be written
* @param returnType the return type of the controller method
* @param selectedContentType the content type selected through content negotiation
* @param selectedConverterType the converter type selected to write to the response
* @param request the current request
* @param response the current response
* @return the body that was passed in or a modified (possibly new) instance
*/
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
log.info("beforeBodyWrite:{},returnType:{},selectedContentType:{},selectedConverterType:{}", body,returnType,
selectedContentType, selectedConverterType);
Method method = returnType.getMethod();
assert method != null;
ChangeBody annotation = method.getAnnotation(ChangeBody.class);
assert annotation != null;
if (body instanceof MyResponse) {
MyResponse res = (MyResponse) body;
Object data = res.getData();
changeField(data);
log.info("替换后的结果 data:{}",data);
res.setData(data);
}
return body;
}
private void changeField(Object data) throws IllegalAccessException {
Class<?> aClass = data.getClass();
//返回所有的字段,但是不包括父类的字段
Field[] fields = aClass.getDeclaredFields();
//返回公共的字段
Field[] publicFields = aClass.getFields();
if(fields.length == 0){
return;
}
for (Field field : fields) {
ChangeField f = field.getAnnotation(ChangeField.class);
if (Objects.isNull(f)){
continue;
}
log.info("替换的字段:{}",field);
field.setAccessible(true);
field.set(data,getFieldValue(f,field,data));
}
}
private Object getFieldValue(ChangeField f,Field field,Object data) throws IllegalAccessException {
log.info("判断执行替换");
int sub = f.value();
Object o = field.get(data);
if (o instanceof String){
String value = (String) o;
if (Objects.isNull(value)){
return o;
}
if (value.length() < sub){
return o;
}
log.info("替换");
//可以采用替换方法,正则表达式替换
return value.substring(0,sub) + "**";
}
return o;
}
}
注解用于标识
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChangeBody {
}
接口使用
使用@ChangeBody进行区分是否执行增强,如果是统一增强,就不需要这样处理
@ChangeBody
@PostMapping("/getOne")
public MyResponse<TestGetOneRes> getOne(@RequestBody TestGeOne in) {
log.info("test/getOne,body:{}", in);
return MyResponse.success(testServcie.getOne(in.getId()));
}
自定义aop进行脱敏
spring的增强就是一种aop,如果无法满足需求,比如需要针对字段注解等,这个时候可以配合spring自带的增强或者自定义aop。
序列化脱敏
针对json的返回,可以使用序列化脱敏,灵活度更高