关于返回String类型得数据多了双引号的坑

  背景:

       通常在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 格式会多带出一对双引号的问题就弄明白了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值