ResponseBodyAdvice用于在返回值写入响应之前,将body的内容重新封装,直接上代码
@Component
public class CommonResponseDataAdvice implements ResponseBodyAdvice<Object> {
private static final String V_3_API_DOCS = "/v3/api-docs";
private static final String SWAGGER_RESOURCES = "/swagger-resources";
private static final String ADMIN_ACTUATOR = "/actuator";
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
if (methodParameter.getAnnotatedElement().isAnnotationPresent(IgnoreResponseAdvice.class)) {
return false;
} else {
return !methodParameter.getMethod().isAnnotationPresent(IgnoreResponseAdvice.class);
}
}
@Nullable
public Object beforeBodyWrite(@Nullable Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//避免对swagger的api接口返回值进行封装
if (((ServletServerHttpRequest) serverHttpRequest).getServletRequest().getRequestURI().equals(V_3_API_DOCS)) {
return o;
}
if (((ServletServerHttpRequest) serverHttpRequest).getServletRequest().getRequestURI().contains(SWAGGER_RESOURCES)) {
return o;
}
//避免对actuator的监控接口进行封装
if (((ServletServerHttpRequest) serverHttpRequest).getServletRequest().getRequestURI().contains(ADMIN_ACTUATOR)) {
return o;
}
if (o instanceof Result) {
return o;
}
return new Result<>(0, "",o);
}
}
关键方法beforeBodyWrite,在SpringMVC主流程有讲到
它将被调用,从而将返回值封装到自定义的Result里面,方便前端统一处理状态码。
在集成swagger之后,始终报错,接口文档出不来,出现如下错误信息:
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: “2.0” and those that match openapi: 3.0.n (for example, openapi: 3.0.0).
百度了半天没有用,最后检查/v3/api-docs接口发现返回值被封装之后,swagger的前端无法解析自定义的结构,导致报错。
解决方案: 判断特定的接口路径不做封装
定义一个接口
@GetMapping("/getUserName")
@ApiOperation("根据ID获取用户")
public String getUserName() {
return "username";
}
使用Swagger发送请求
报错了,说是Result无法强转为String,没办法只能从源码找解决方案,根据SpringMVC主流程,关键地方还是在这里
Spring提供了10个消息转换器,String在Json的前面。调用beforeBodyWrite方法之后
先匹配到转换器StringHttpMessageConverter,又将body封装成Result类型
StringHttpMessageConverter的泛型是String,进入write方法之后
t的类型是Result,子类中的addDefaultHeaders方法需要的是String
导致类型强转异常
解决方案: 接口的返回String类型时,提前用Result封装。不能在beforeBodyWrite判断是StringHttpMessageConverter就不封装,因为StringHttpMessageConverter转换完就返回,Json转换器就用不上了。
@GetMapping("/getUserName")
@ApiOperation("根据ID获取用户")
public Result<String> getUserName() {
return new Result<>("username");
}