背景:
通常在sprinboot 中都是使用@RestController 作为和前端的交互,将返回的对象json 化。有个场景只需要返回对应的字符串信息即可。
正常情况下使用springboot 默认的 WebMvcConfigurationSupport 配置,支持正常的json 和字符串。
产生字符串多了双引号的问题原因:
由于很多场景下为了解决 时间类型的数据和前端交互的格式统一处理。很多从网上会找到继承WebMvcConfigurationSupport 改造返回的时间格式的一种方式。(以下只贴了一部分代码)因为改写了这个报文转换器的配置,导致返回的字符串多了一对双引号
MvcConfiguration extends WebMvcConfigurationSupport {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
converters.add(jackson2HttpMessageConverter());
}
/**
* 时间格式转换器,将Date类型统一转换为yyyy-MM-dd HH:mm:ss格式的字符串
* 长整型转换成String
* 忽略value为null时key的输出
*/
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = serializingObjectMapper();
// 序列化枚举值
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
//忽略value为null时key的输出
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
converter.setObjectMapper(objectMapper);
return converter;
}
}
解决方案: 增加一个处理字符串的转化器,如下:可参考
SpringBoot 返回纯字符串的时候,多了双引号的问题解决_返回结果有两个引号_桑汤奈伊伏的博客-CSDN博客注:一定要在请求接口上加上产生的消息类型注解 produces = "text/plain", 这样消息才会与对应的转换器对应上处理
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);
//字符串转换器
StringHttpMessageConverter stringHttpMessageConverter = getStringHttpMessageConverter();
converters.add(stringHttpMessageConverter);
converters.add(jackson2HttpMessageConverter());
}
/**
* 字符串转化器
* @return
*/
public StringHttpMessageConverter getStringHttpMessageConverter() {
List<MediaType> listString = new ArrayList<MediaType>();
//字符串的消息类型为text/plain
listString.add(MediaType.TEXT_PLAIN);
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setSupportedMediaTypes(listString);
return stringHttpMessageConverter;
}
------------------------------------问题已经解决,我们来看看更深层次的原因------ ----------------------------
1.先从springboot 的自动装配说起:
spingboot 在默认情况下使用 WebMvcAutoConfiguration 自动装配mvc配置,在WebMvcAutoConfiguration 实现中有个 @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})即:
当容器里没有指定 WebMvcConfigurationSupport 的情况下,使用WebMvcAutoConfiguration。
但是我们因为要统一处理json 转化的格式,继承了WebMvcConfigurationSupport 重新实现初始化HttpMessageConverter(报文信息转换器 );所以该WebMvcAutoConfiguration 自动装配就失效了。
2.看看整个http 请求的处理链路:
在mvc 框架下会统一经过DispatcherServlet->doService()->doDispatch()->getHandlerAdapter()->handle()(处理,controller)->ModelAndView 通过获取对应的适配器来做为整个请求的处理。其中我们所需要的HttpMessageConverter,报文信息转换器,就在对应的适配器里定义好了。
2.1 通常我们都是使用注解的方式写Controller 层,所以用到的适配器是RequestMappingHandlerAdapter;
我们看在没有改动的情况下,使用自动装配模式的RequestMappingHandlerAdapter实现逻辑:直接继承WebMvcConfigurationSupport 使用了Support 的实现。
2.2 那我们进一步看看RequestMappingHandlerAdapter 的实现逻辑:
2.3 在获取报文转换器的实现中,看到了我们之前重写的方法,也就是在这里,我们把对应的转换器初始化替换了。同时,因为设置转换器后,messageConverters 不为空。所以不会再给我们添加默认的报文转换器。这也是为什么我们不重写时不会存在转换问题,重写之后的没有对应的StringHttpMessageConverter 字符串转换器了;
我们自己重写的添加自定义转换器:
默认的转换器初始化实现:
已上我们已经弄清楚为什么默认装载的模式下处理字符串没有问题。重写之后处理就存在问题了。本质就是报文转化器被重写了,字符串转化器丢失了。
-----还有一个问题没有解决? 如果没有字符串转换器?为什么返回的字符串会多加了双引号?------
接上文,自定义设置报文转换器,只设置了一个MappingJackson2HttpMessageConverter,导致原有设置的字符串处理器未加载,只能使用MappingJackson2HttpMessageConverter 处理字符串的返回了。
对于HttpMessageConverter 解析流程可以参考:
SpringMVC消息转换器HttpMessageConverter_springmvc消息转换器使用_梁云亮的博客-CSDN博客
在controller处理完业务逻辑后,会返回ModelAndView,在该过程中returnValueHandlers.handleReturnValue->writeWithMessageConverters 处理返回的数据,这时候就要用到我们设置的报文转化器.
通过获取可用的和可接受的请求头信息,再根据权重,选择出最终的请求头类型。然后根据请求头类型和注入的转化器匹配,决定使用何种报文转化器来转换数据。
【SpringBoot2—Web请求返回值处理源码解析】_writeinternal_zhiguo.zheng的博客-CSDN博客 说清楚返回消息的流程,如何匹配使用的转换器。
调用write 方法。设置对应的请求头信;
调用writeValue 的方法将java 类型的数据 序列化成json 格式,所以在这个方法里面就要选择序列化的方式。
根据java传入的数据类型,选择对应的序列化实现方式:
MappingJackson2HttpMessageConverter 中为什么会把字符串多加了双引号呢?
这个需要从Jackson 和java 类的相互转换说起。实际上就是看java类转化成json 的时候选择用什么序列化的方式。
从源码看,在序列化的时候首先会判断传入的类型从而选择对应的序列化实现,我们传入的是字符串,所以选择的是StringSerializer。在写入的时候,前后加了"",所以用这个MappingJackson2HttpMessageConverter 返回字符串的时候就多了一对双引号。
结束:至此从为什么改了配置类的实现方式和为什么选用了json 转化器再到为什么字符串转换成json 格式会多带出一对双引号的问题就弄明白了。