补充这篇博文是因为 在SpringBoot之HandlerInterceptor拦截器的使用 ——(一) 中提到 postHandle 方法有机会修改ModelAndView ,但是实际情况却是 无法修改 。原因是 @ResponseBody 注释或者返回 ResponseEntity的方法在先于postHandle方法之前将响应提交给HandlerAdapter(调用handler和Interceptor方法者),所以之后的修改就无效了。那么在这里我将采用实现 ResponseBodyAdvice 接口的方式来 修改ResponseBody 。
ResponseBodyAdvice简介
ResponseBodyAdvice接口,其实是对加了 @RestController (也就是 @Controller + @ResponseBody )注解的处理器将要返回的值进行 增强处理 。
可以用于规范化前后端调用的响应格式,也可以用作统一签名等操作。
ResponseBodyAdvice其实也就是采用了 AOP 的思想,对返回值进行一次修改。
ResponseBodyAdvice方法介绍
public interface ResponseBodyAdvice<T> {
// 该方法可以用于制定准入规则,只有当该方法返回True时,才会进入beforeBodyWrite方法
boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
// var1 也就是我们的返回值,主要就是针对其进行修改
@Nullable
T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}
自定义我们的ResponseBodyAdvice
- 假定我这里去实现一个统一签名,然后返回给接口调用方的处理器
package com.zhibo.core;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.zhibo.common.response.Response;
import com.zhibo.security.util.MD5Util;
import com.zhibo.core.annotation.SignAdvice;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.lang.reflect.Method;
import java.util.Objects;
@Slf4j
@ControllerAdvice("com.zhibo.controller.v3")// 此处可以限制需要拦截的包路径,不填写为全部拦截进入supports 方法验证
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Response> {
//签名KEY
private final static String SIGN_KEY = "zhibo_lv";
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
//根据自定义注解判断方法上是否有指定需要拦截,当然这里也可以什么都不写直接返回true,根据自身业务场景做限制校验即可
Method method = methodParameter.getMethod();
SignAdvice ra = method.getAnnotation(SignAdvice.class);
return Objects.nonNull(ra);
}
@Override
public Response beforeBodyWrite(Response rsp, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (null == rsp) return null;
// 打印未加签名前的Response
log.info("未加签名前的Response: {}",JSON.toJSONString(rsp));
SignResponse signRsp = new SignResponse(rsp);
signRsp.setDateTime(System.nanoTime());
//自己随便弄个加密算法
StringBuilder sbd = new StringBuilder();
SignAdvice ra = methodParameter.getMethod().getAnnotation(SignAdvice.class);
sbd.append(signRsp.getCode())
.append(signRsp.getDateTime())
.append(signRsp.getMessage())
.append(JSON.toJSONString(signRsp.getResult(), SerializerFeature.MapSortField))
.append(StringUtils.isBlank(ra.signKey()) ? SIGN_KEY : ra.signKey());
signRsp.setSign(MD5Util.md5(sbd.toString()));
log.info("添加签名后的Response: {}",JSON.toJSONString(signRsp));
return signRsp;
}
}
自定义注解类
package com.zhibo.core.annotation;
import javax.ws.rs.NameBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author <a href="mailto:zhibo.lv@xxxxxxxx.com">zhibo.lv</a>
* Response自动签名
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@NameBinding
public @interface SignAdvice
{
/**
* 自定义签名KEY
* 如不指定则使用系统默认KEY
*/
String signKey() default "";
}
Controller测试代码
package com.zhibo.controller.v3;
import com.zhibo.common.response.Response;
import com.zhibo.core.annotation.SignAdvice;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@ResponseBody
@Api(tags = "测试SignResponse", description = "测试SignResponse")
public class SignResponseController {
@SignAdvice(signKey="signKey:zhibo testDemo")
@GetMapping(value="/v3/noauth/sign/test")
@ApiOperation(value = "/v3/noauth/sign/test", notes = "测试SignResponse")
public Response<String> testDemo() {
Response<String> response = new Response<>();
response.setCode(0);
response.setMessage("Success");
response.setResult("点赞三连你最帅");
return response;
}
}
调用接口执行结果![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8090126f3bae4bc3ae3e73321da28bd2.png)
你的点赞就是我创作的最大动力,如果写的不错,来个三连行不行