背景:
ResponseBodyAdvice 顾名思义是返回body体通知器 ,其作用是让我们在写controller层时,可以不用每个路由都写相对统一的返回对象.类似:
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(body instanceof HttpResult){
return body;
}
return HttpResult.success(body);
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
public class HttpResult<T> implements Serializable {
private int status;
private String info;
private T data;
}
问题:
当返回结果为String时,会报错:
@GetMapping("test")
public String test() {
return "i am a string";
}
报错信息:
17:48:22.403 [XNIO-1 task-1] ERROR ServiceExceptionHandler: - 【ServiceExceptionHandler - Exception】
[java.lang.ClassCastException: class com.gaodun.storm.passport.common.HttpResult cannot be cast to class java.lang.String (com.gaodun.storm.passport.common.HttpResult is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')
at org.springframework.http.converter.StringHttpMessageConverter.getContentLength(StringHttpMessageConverter.java:43)
at org.springframework.http.converter.AbstractHttpMessageConverter.addDefaultHeaders(AbstractHttpMessageConverter.java:260)
分析:
很显然,问题出在消息转换器上,为什么会用到 StringHttpMessageConverter 而不是我们配置的,如MappingJackson2HttpMessageConverter 转换器,或者是fastjson转换器.
贴出我们自定义的消息转换器配置代码:
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter
= new MappingJackson2HttpMessageConverter();
//设置日期格式
ObjectMapper objectMapper = new ObjectMapper();
SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
objectMapper.setDateFormat(smt);
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
//设置中文编码格式
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
mediaTypes.add(MediaType.TEXT_HTML);
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(mediaTypes);
return mappingJackson2HttpMessageConverter;
}
跟进源码发现:
spring 会根据 mediaType 来 选取最合适的消息转换器来转换消息体.
有一个消息转换器集合,从第一个开始,依次遍历,只要在遍历的过程中,发现有mediaType符合条件,即选定消息体转换器,哪怕不是我们想要的,哪怕后面还有更合适的消息转换器,都不会再执行了.
很不幸,我们的 StringHttpMessageConverter 被选中了,于是乎就出现了偏头的那个错误.
解决方案1:
比较拙劣,不改变那个错误的消息体转换器,在 beforeBodyWrite 方法内将返回值强行用json转换为String. 可以解决问题,但是手段拙劣,不推荐.
解决方案2:
spring 其实通过 WebMvcConfigurer留了一个扩展点给开发人员自定义消息体集合的操作
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, mappingJackson2HttpMessageConverter());
}
至此,问题解决.希望可以帮助到遇到相同问题的小伙伴。