背景:

       通常在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;
  }
 }

解决方案: 增加一个处理字符串的转化器,如下:可参考

注:一定要在请求接口上加上产生的消息类型注解 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 自动装配就失效了。

springboot 返回 js springboot返回json 会有引号吗_字符串

2.看看整个http 请求的处理链路:

       在mvc 框架下会统一经过DispatcherServlet->doService()->doDispatch()->getHandlerAdapter()->handle()(处理,controller)->ModelAndView 通过获取对应的适配器来做为整个请求的处理。其中我们所需要的HttpMessageConverter,报文信息转换器,就在对应的适配器里定义好了。

springboot 返回 js springboot返回json 会有引号吗_spring boot_02

       2.1 通常我们都是使用注解的方式写Controller 层,所以用到的适配器是RequestMappingHandlerAdapter;

      我们看在没有改动的情况下,使用自动装配模式的RequestMappingHandlerAdapter实现逻辑:直接继承WebMvcConfigurationSupport 使用了Support 的实现。

springboot 返回 js springboot返回json 会有引号吗_字符串_03

      2.2 那我们进一步看看RequestMappingHandlerAdapter 的实现逻辑:

springboot 返回 js springboot返回json 会有引号吗_spring boot_04

           2.3 在获取报文转换器的实现中,看到了我们之前重写的方法,也就是在这里,我们把对应的转换器初始化替换了。同时,因为设置转换器后,messageConverters 不为空。所以不会再给我们添加默认的报文转换器。这也是为什么我们不重写时不会存在转换问题,重写之后的没有对应的StringHttpMessageConverter 字符串转换器了;

springboot 返回 js springboot返回json 会有引号吗_java_05

我们自己重写的添加自定义转换器:

springboot 返回 js springboot返回json 会有引号吗_java_06

默认的转换器初始化实现:

springboot 返回 js springboot返回json 会有引号吗_springboot 返回 js_07

       已上我们已经弄清楚为什么默认装载的模式下处理字符串没有问题。重写之后处理就存在问题了。本质就是报文转化器被重写了,字符串转化器丢失了。

-----还有一个问题没有解决? 如果没有字符串转换器?为什么返回的字符串会多加了双引号?------

       接上文,自定义设置报文转换器,只设置了一个MappingJackson2HttpMessageConverter,导致原有设置的字符串处理器未加载,只能使用MappingJackson2HttpMessageConverter 处理字符串的返回了。

对于HttpMessageConverter 解析流程可以参考

       在controller处理完业务逻辑后,会返回ModelAndView,在该过程中returnValueHandlers.handleReturnValue->writeWithMessageConverters 处理返回的数据,这时候就要用到我们设置的报文转化器.

       通过获取可用的和可接受的请求头信息,再根据权重,选择出最终的请求头类型。然后根据请求头类型和注入的转化器匹配,决定使用何种报文转化器来转换数据。

说清楚返回消息的流程,如何匹配使用的转换器。

springboot 返回 js springboot返回json 会有引号吗_springboot 返回 js_08

调用write 方法。设置对应的请求头信;

springboot 返回 js springboot返回json 会有引号吗_springboot 返回 js_09

调用writeValue 的方法将java 类型的数据 序列化成json 格式,所以在这个方法里面就要选择序列化的方式。

springboot 返回 js springboot返回json 会有引号吗_字符串_10

根据java传入的数据类型,选择对应的序列化实现方式:

springboot 返回 js springboot返回json 会有引号吗_springboot 返回 js_11

       MappingJackson2HttpMessageConverter 中为什么会把字符串多加了双引号呢?

      这个需要从Jackson 和java 类的相互转换说起。实际上就是看java类转化成json 的时候选择用什么序列化的方式。

       从源码看,在序列化的时候首先会判断传入的类型从而选择对应的序列化实现,我们传入的是字符串,所以选择的是StringSerializer。在写入的时候,前后加了"",所以用这个MappingJackson2HttpMessageConverter 返回字符串的时候就多了一对双引号。

springboot 返回 js springboot返回json 会有引号吗_mvc_12

springboot 返回 js springboot返回json 会有引号吗_springboot 返回 js_13

 

 结束:至此从为什么改了配置类的实现方式和为什么选用了json 转化器再到为什么字符串转换成json 格式会多带出一对双引号的问题就弄明白了。