@RestControllerAdvice ResponseBodyAdvice
使用 @RestControllerAdvice
和 ResponseBodyAdvice<Object>
返回 String 类型的报错
问题描述:在 controller 层 我只想关注返回的业务数据 而不想处理通用的响应格式,所以我用了
@RestControllerAdvice
和ResponseBodyAdvice<Object>
来处理返回参数的包装,但是在返回String
类型的数据时报错:class com.imooc.ecommerce.vo.CommonResponse cannot be cast to class java.lang.String (com.imooc.ecommerce.vo.CommonResponse is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')
无法将通用的返回类映射到 String 类型上
controller 返回 advice
/**
* @author qingtian
* @description:
* @Package com.imooc.ecommerce.advice
* @date 2021/11/30 22:52
*/
@RestControllerAdvice(value = "com.imooc.ecommerce")
public class CommonResponseDataAdvice implements ResponseBodyAdvice<Object> {
/**
* 判断响应是否需要被包装
*/
@Override
public boolean supports(MethodParameter methodParameter,
Class<? extends HttpMessageConverter<?>> aClass) {
//如果被IgnoreResponseAdvice注解进行标记则不需要对返回的响应体做处理
if (methodParameter.getDeclaringClass()
.isAnnotationPresent(IgnoreResponseAdvice.class)) {
return false;
}
if (methodParameter.getMethod()
.isAnnotationPresent(IgnoreResponseAdvice.class)) {
return false;
}
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter,
MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
//定义最终返回的对象
CommonResponse<Object> response = new CommonResponse<>(0,"");
if (null == o) {
return response;
}else if (o instanceof CommonResponse) {
response = (CommonResponse<Object>) o;
}else {
response.setData(o);
}
return response;
}
}
controller 层代码
/**
* @author Guank
* @version 1.0
* @description: 测试缓存 request 中的 body
* @date 2022-08-11 23:47
*/
@RestController
@RequestMapping("/cache")
@Slf4j
public class RequestCacheController {
@PostMapping("/requestCache")
public String requestCache(@RequestBody UsernameAndPassword usernameAndPassword) {
return usernameAndPassword.getPassword();
}
问题分析
-
断点打到这里 看见
HttpMessageConverter
这个参数类型传入的是class org.springframework.http.converter.StringHttpMessageConverter
怀疑是
Converter
类型的原因,接着 debug
-
AbstractMessageConverterMethodProcessor
类的这个方法中看到此时的 body 是
CommonResponse
类型,valueType
是String
类型的。接着往下看第229行代码,此时
converter
是StringHttpMessageconverter
,但是我的 body 是CommonResponse
类型,这里发生了转换失败 -
查看为什么
converter
是StringHttpMessageconverter
看到第188行代码,
converter
初始化,在第196行对其进行赋值,推测可能根据这个方法开头的value
和valueType
有关,它发现value
是String
类型的,然后将converter
设置成了StringHttpMessageconverter
但是我们在对返回参数进行包装的时候已经将其包装成了CommonResponse
,所以出现转换失败。
解决方法
在所有的
HttpMessageConverter
实例集合中,StringHttpMessageConverter
要比其它的Converter
排得靠前一些。我们需要将处理 Object 类型的HttpMessageConverter
放得靠前一些。可以在configuration
中完成。
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0,new MappingJackson2HttpMessageConverter());
}
}
评论区