当前位置:   article > 正文

FastJson升级踩坑记_fastjson1.2.73和那个版本去贴切

fastjson1.2.73和那个版本去贴切

事因

前段时间网上爆出FastJson漏洞后就接到公司安全部门硬性通知的全司升级,要求所有的fastjson升级到1.2.73版本,并且在发布系统进行jar包版本拦截,低于这个版本不让发布。这招比较狠!于是开启了升级之路。大家都想到了升级可能会有风险,都比较忐忑,但事还得做。

发现问题

幸运的是升级了好几个服务都顺利升级了,还挺开心的,觉得不用担心了。剩下最后一个老服务升级了。这天和其他服务升级一样也没有太担心,正常上线了一台机器。本来不想看日志检测是否有问题的,过了会觉得还是看看比较放心点。打开日志系统后发现有几条ERROR日志报’Content-Type’ can not contain wildcard type ‘*’。

[2020-12-16 14:13:58.266] [http-nio-8092-exec-9] [ERROR] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet]:181 x:() - Servlet.service() for servlet [dispatcherServlet] 
in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: 'Content-Type' cannot contain wildcard type '*'] with root cause
java.lang.IllegalArgumentException: 'Content-Type' cannot contain wildcard type '*'
  • 1
  • 2
  • 3

心里想可能以前也有报这个错误,搜索没有发布的机器上的日志,没有发现这个错误。惊呆了,升级还是踩坑了,赶紧拉下线。还好虽然有少量报错,业务还没有感知,幸亏去检查了,发现及时没有吃个线上事件。

疑惑

因为是Get请求,拷贝了同样的请求放在浏览器里请求了报错的机器,发现接口正常返回。看了下http请求的头文件如下:
1.png
2.png

难道业务方的代码里header指定的Accept 特殊?可是通过线上的日志已经nginx的日志里查看到请求的Header信息。
先看了下代码,这个接口是Get请求,RequestMapping注解里没有指定produces和consumes的值也没有打@ResponseBody的注解。
要解决问题就得在本地复现问题。令我失望的是不管用浏览器直接调用,还是postman指定Header的Accept的各种值都是正常返回结果。用同样的方式在测试环境尝试也都是正常的,没有报错。连复现都不行,难道线上的报错是偶然导致的?那找测试人员在预发布用postman试试,令我意想不到的是还真复现了。看来上线前测试人员在预发布没有常规测试啊,如果常规测试肯定是可以发现这个问题的。于是去测试人员那里看了下请求的Header,发现postman默认的Accept是*/*。fastjson版本回退后在预发布重新发布,同样的请求就可以正常返回了,但是以同样的方式和高版本的fastjson在测试环境和本地测试,结果是正常的。这就令人费解了。在本地不能复现要去debug就比较难了。

分析

问题1:升级后为什么报错

还是先看看源码分析下吧。
应用配做的HttpMessageConverter只配置了StringHttpMessageConverter 和FastJsonHttpMessageConverter

@Configuration
public class FastJsonHttpMessageConverterConfig {
  /**
   添加JSON序列化方式,默认是Jackson,替换为FastJson
   @return:HttpMessageConverters
   */
  @Bean
  public HttpMessageConverters injectFastJsonHttpMessageConverter(){

    FastJsonHttpMessageConverter fastJsonHttpMessageConverter=new FastJsonHttpMessageConverter();
    FastJsonConfig fastJsonConfig=new FastJsonConfig();
    fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
    fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    stringHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
    List converters=new ArrayList();
    converters.add(stringHttpMessageConverter);
    converters.add(fastJsonHttpMessageConverter);
    return new HttpMessageConverters(false,converters);

  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters()方法,具体逻辑可以参考代码注解

protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
  省略部分代码//
		HttpServletRequest request = inputMessage.getServletRequest();
 
  //从request的Header里获取Accept的值,可以参考
 //org.springframework.web.accept.HeaderContentNegotiationStrategy#resolveMediaTypes
 //前面已经分析过了,对应我的情况Header的Accept是*/*,所以requestedMediaTypes是MediaType.ALL
		List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
		//获取支持的MediaTypes
		//1、先从request的attribute里获取@RequestMapping注解的produces配置的值,如果配置了就返回配置的MediaTypes,否则转第2步
		//2、从所有配置的HttpMessageConverter里匹配canWrite该返回值的Converter,并获取对应Converter SupportedMediaTypes的值
		//应用的实际情况分析开始/
		//对应我的情况@RequestMapping没有配置produces,那么应该取所有HttpMessageConverter的SupportedMediaTypes。
		//根据AbstractHttpMessageConverter的getSupportedMediaTypes源码可以知道supportedMediaTypes的值是在HttpMessageConverter实例化通过构造方法参数传入的
		//FastJsonHttpMessageConverter是MediaType.ALL
		//StringHttpMessageConverter是MediaType.TEXT_PLAIN, MediaType.ALL
		//而这两个Converter能canWrite对象的就只有FastJsonHttpMessageConverter
		//所以producibleMediaTypes就只有MediaType.ALL也就是*/*
		应用的实际情况分析结束/
		List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
     省略部分代码//
		Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
		//从ProducibleMediaTypes里选择可以兼容requestedType的MediaType的类型
		for (MediaType requestedType : requestedMediaTypes) {
			for (MediaType producibleType : producibleMediaTypes) {
				if (requestedType.isCompatibleWith(producibleType)) {
					compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
				}
			}
		}
        省略部分代码//
		List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
		//对可以支持requestedType即Header的Accept的所有MediaType进行排序
		// */*的MediaType会排在其他类型的后面
		MediaType.sortBySpecificityAndQuality(mediaTypes);

		MediaType selectedMediaType = null;
		for (MediaType mediaType : mediaTypes) {
		//从排序后的MediaTypes里选择一个正确的MediaType
		//根据isConcrete代码逻辑是非*/*的MediaType就是正确的
			if (mediaType.isConcrete()) {
			//如果MediaType不是*/*则返回该MediaType
				selectedMediaType = mediaType;
				break;
			}
			//如果该MediaType是MediaType.ALL或者MEDIA_TYPE_APPLICATION,则selectedMediaType=MediaType.APPLICATION_OCTET_STREAM
			//应用的实际情况分析开始/
			//从上面的分析看我的应用的实际情况是mediaType只要MediaType.ALL
			//所以这里selectedMediaType会被赋值为MediaType.APPLICATION_OCTET_STREAM
			///应用的实际情况分析结束///
			else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
				selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
				break;
			}
		}
//如果selectedMediaType!=null,则从所有配置的HttpMessageConverter里选择一个可以支持selectedMediaType类型的Converter,并调用对应的write方法
		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
				if (messageConverter instanceof GenericHttpMessageConverter) {
	//省略部分代码
	//并调用对应的write方法并传入selectedMediaType
	//应用的实际情况分析开始/
	//selectedMediaType=MediaType.APPLICATION_OCTET_STREAM
	///应用的实际情况分析结束///
							((GenericHttpMessageConverter) messageConverter).write(
									outputValue, declaredType, selectedMediaType, outputMessage);
	//省略部分代码
	}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

下面是fastjson的1.2.73FastJsonHttpMessageConverter的源码,可以看到调用了super.write()方法。
com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter.write()

    /**
     * Can serialize/deserialize all types.
     */
    public FastJsonHttpMessageConverter() {

        super(MediaType.ALL);
    }
    public void write(Object o, Type type, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
    //应用的实际情况分析开始/
	//contentType=MediaType.APPLICATION_OCTET_STREAM
	///应用的实际情况分析结束///
        super.write(o, contentType, outputMessage);// support StreamingHttpOutputMessage in spring4.0+
        //writeInternal(o, outputMessage);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

org.springframework.http.converter.AbstractHttpMessageConverter#write
方法里调用了addDefaultHeaders方法

	public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
    //应用的实际情况分析开始/
	//contentType=MediaType.APPLICATION_OCTET_STREAM
	///应用的实际情况分析结束///
		final HttpHeaders headers = outputMessage.getHeaders();
		addDefaultHeaders(headers, t, contentType);
		//省略部分代码///
		}
		else {
			writeInternal(t, outputMessage);
			outputMessage.getBody().flush();
		}
	}
	///addDefaultHeaders方法
protected void addDefaultHeaders(HttpHeaders headers, T t, MediaType contentType) throws IOException{
      //如果Header的ContentType==null
     //应用的实际情况分析开始/
	//contentType=MediaType.APPLICATION_OCTET_STREAM
	//Header的Content-Type也是null
	///应用的实际情况分析结束///
		if (headers.getContentType() == null) {
			MediaType contentTypeToUse = contentType;
			/**
			*如果传入的contentType==null或者则contentType是 */*
			*则从默认支持的MediaType里选择第一个MediaType
			*/
			if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
				contentTypeToUse = getDefaultContentType(t);
			}
			/**
			 *如果传入的contentType是application/octet-stream
			 *则从默认支持的MediaType里选择第一个MediaType
			 *如果默认的选出的MediaType==null就使用传入的contentType
			 */
			//应用的实际情况分析开始/
			//contentType=MediaType.APPLICATION_OCTET_STREAM
			//这个时候就会调用getDefaultContentType而FastJsonConvert的default值就是MediaType.ALL,即contentTypeToUse=MediaType.ALL
			///应用的实际情况分析结束///
			else if (MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) {
				MediaType mediaType = getDefaultContentType(t);
				contentTypeToUse = (mediaType != null ? mediaType : contentTypeToUse);
			}
			if (contentTypeToUse != null) {
				if (contentTypeToUse.getCharset() == null) {
					Charset defaultCharset = getDefaultCharset();
					if (defaultCharset != null) {
						contentTypeToUse = new MediaType(contentTypeToUse, defaultCharset);
					}
				}
				//设置Header的ContentType为contentTypeToUse
			   //应用的实际情况分析开始/
               //contentTypeToUse=MediaType.ALL
			  ///应用的实际情况分析结束
				headers.setContentType(contentTypeToUse);
			}
		}
		if (headers.getContentLength() < 0 && !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
			Long contentLength = getContentLength(t, headers.getContentType());
			if (contentLength != null) {
				headers.setContentLength(contentLength);
			}
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

org.springframework.http.converter.AbstractHttpMessageConverter#getDefaultContentType

private List<MediaType> supportedMediaTypes = Collections.emptyList();
	
	protected MediaType getDefaultContentType(T t) throws IOException {
		List<MediaType> mediaTypes = getSupportedMediaTypes();
		return (!mediaTypes.isEmpty() ? mediaTypes.get(0) : null);
	}
	//这里的supportedMediaTypes属性是在创建HttpMessageConverter实例时通过构造方法传入的,
	//而FastJsonHttpMessageConverter的构造方法是传的MediaType.ALL而MediaType.ALL对应的就是 "*/*"
	@Override
	public List<MediaType> getSupportedMediaTypes() {
		return Collections.unmodifiableList(this.supportedMediaTypes);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

org.springframework.http.HttpHeaders#setContentType
这里就是报’Content-Type’ cannot contain wildcard type '*'错误的源头了。

	/**
	 * Set the {@linkplain MediaType media type} of the body,
	 * as specified by the {@code Content-Type} header.
	 */
	public void setContentType(MediaType mediaType) {
	 //如果mediaType是*/*则报错
	  //应用的实际情况分析开始/
      //传入的mediaType=MediaType.ALL,所以报错
	  ///应用的实际情况分析结束
		Assert.isTrue(!mediaType.isWildcardType(), "'Content-Type' cannot contain wildcard type '*'");
		Assert.isTrue(!mediaType.isWildcardSubtype(), "'Content-Type' cannot contain wildcard subtype '*'");
		set(CONTENT_TYPE, mediaType.toString());
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

从上面的源码分析到新版的FastJsonHttpMessageConverter的构造方法是传的MediaType.ALL而MediaType.ALL对应的就是 "*/*"难道是新版本这里修改了,于是看了老版本的,结果让人失望。跟现在的版本一样的。
3.png
哪为什么升级前的没有报错呢??
再在看看老版本的write方法。下面是1.2.33版本FastJsonHttpMessageConverter的write方法

/*
     * @see org.springframework.http.converter.GenericHttpMessageConverter#write(java.lang.Object, java.lang.reflect.Type, org.springframework.http.MediaType, org.springframework.http.HttpOutputMessage)
     */
    public void write(Object t, //
                      Type type, //
                      MediaType contentType, //
                      HttpOutputMessage outputMessage //
    ) throws IOException, HttpMessageNotWritableException {

        HttpHeaders headers = outputMessage.getHeaders();
        if (headers.getContentType() == null) {
            if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
                contentType = getDefaultContentType(t);
            }
            if (contentType != null) {
                headers.setContentType(contentType);
            }
        }
        if (headers.getContentLength() == -1) {
            Long contentLength = getContentLength(t, headers.getContentType());
            if (contentLength != null) {
                headers.setContentLength(contentLength);
            }
        }
        writeInternal(t, outputMessage);
        outputMessage.getBody().flush();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

这个方法1.2.73版的不一样,1.2.73版的是直接调用了AbstractHttpMessageConverter#write方法,而1.2.33版本是完全重写了父类的方法自己实现的。但粗略的看和AbstractHttpMessageConverter#write前面的逻辑一样也是判断ContentType然后设置headers.setContentType(contentType),但是细看就会发现秘密。1.2.33版本重写的地方是如果传入的contentType!=null 就直接设置,而通过前面的分析这时的contentType=MediaType.APPLICATION_OCTET_STREAM,所以headers.setContentType(contentType)(这个方法是报错的地方)的时候不是*/*,不会报错,而AbstractHttpMessageConverter#write方法的addDefaultHeaders方法里是增加了如下代码,当contentType=MediaType.APPLICATION_OCTET_STREAM是获取了FastJsonHttpMessageConverter的默认的MediaType.ALL

else if (MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) {
				MediaType mediaType = getDefaultContentType(t);
				contentTypeToUse = (mediaType != null ? mediaType : contentTypeToUse);
			}
			if (contentTypeToUse != null) {
				if (contentTypeToUse.getCharset() == null) {
					Charset defaultCharset = getDefaultCharset();
					if (defaultCharset != null) {
						contentTypeToUse = new MediaType(contentTypeToUse, defaultCharset);
					}
				}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

通过上面的一顿分析终于找到了升级FastJson报错的真正原因,找到原因了就好解决了,但是还有个问题在困扰这我。那就是前面说了本地很测试环境为什么不报错??

问题2:为什么本地&测试环境不报错

通过字节码插桩技术,在代码了加入了日志通过本地与预发布的的日志对比,发现本地环境的messageConverters比线上的多了一个MappingJackson2HttpMessageConverter。而MappingJackson2HttpMessageConverter支持的MediaType是MediaType.APPLICATION_JSON, new MediaType(“application”, “*+json”),所以在org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#getProducibleMediaTypes()的时候获取到的producibleMediaTypes就是
在这里插入图片描述
而不只是*/*,排序后
在这里插入图片描述
所以后面的设置header就不会报错。

问题3:为什么本地&测试环境会多一个MappingJackson2HttpMessageConverter呢

通过Debug发现在应用启动是有个BeanPostProcessor springfox.documentation.spring.web.ObjectMapperConfigurer在会postProcessBeforeInitialization里会加入MappingJackson2HttpMessageConverter

  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

    if (bean instanceof RequestMappingHandlerAdapter) {
      RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
      adapter.setMessageConverters(configureMessageConverters(adapter.getMessageConverters()));
    }
    return bean;
  }
  private List<HttpMessageConverter<?>> configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    Iterable<MappingJackson2HttpMessageConverter> jackson2Converters = jackson2Converters(converters);
    if (Iterables.size(jackson2Converters) > 0) {
      for (MappingJackson2HttpMessageConverter each : jackson2Converters) {
        fireObjectMapperConfiguredEvent(each.getObjectMapper());
      }
    } else {
      converters.add(configuredMessageConverter());
    }
    return newArrayList(converters);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

那为什么只有本地和测试环境会有添加这个MappingJackson2HttpMessageConverter呢?难道哪里有什么配置?
通过查看ObjectMapperConfigurer的包名发现是来自于springfox-spring-web,通过查看包依赖发现这个包是属于springfox-swagger2引入的。
突然就豁然开朗了,我在配置swagger时指定了适用于"dev",“test”,

@Profile({"dev","test"})
@Configuration
@EnableSwagger2
public class Swagger2 {}
  • 1
  • 2
  • 3
  • 4

profile 是dev或者test时@EnableSwagger2注解被激活,同时@Import({Swagger2DocumentationConfiguration.class})就被激活

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({Swagger2DocumentationConfiguration.class})
public @interface EnableSwagger2 {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

@Import({Swagger2DocumentationConfiguration.class})被激活时SpringfoxWebMvcConfiguration.class被激活

@Configuration
@Import({ SpringfoxWebMvcConfiguration.class, SwaggerCommonConfiguration.class })
@ComponentScan(basePackages = {
    "springfox.documentation.swagger2.readers.parameter",
    "springfox.documentation.swagger2.web",
    "springfox.documentation.swagger2.mappers"
})
public class Swagger2DocumentationConfiguration {
  @Bean
  public JacksonModuleRegistrar swagger2Module() {
    return new Swagger2JacksonModule();
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

SpringfoxWebMvcConfiguration.class被激活时就会注入BeanPostProcessor的ObjectMapperConfigurer实例

@EnableAspectJAutoProxy
public class SpringfoxWebMvcConfiguration {

  @Bean
  public Defaults defaults() {
    return new Defaults();
  }

  @Bean
  public DocumentationCache resourceGroupCache() {
    return new DocumentationCache();
  }

  @Bean
  public static ObjectMapperConfigurer objectMapperConfigurer() {
    return new ObjectMapperConfigurer();
  }

  @Bean
  public JsonSerializer jsonSerializer(List<JacksonModuleRegistrar> moduleRegistrars) {
    return new JsonSerializer(moduleRegistrars);
  }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

终于所有的疑惑都清楚了。

解决

  1. 可以在@RequestMapping注解里加入produces = “application/json;charset=UTF-8”

  2. 在FastJsonHttpMessageConverter设置支持的MediaType替换默认的MediaType.ALL

List<MediaType> supportMediaTypeList = new ArrayList<>();
    supportMediaTypeList.add(MediaType.TEXT_HTML);
    supportMediaTypeList.add(MediaType.APPLICATION_JSON);
    supportMediaTypeList.add(MediaType.APPLICATION_JSON_UTF8);
    supportMediaTypeList.add(MediaType.valueOf("application/*+json"));
    supportMediaTypeList.add(MediaType.ALL);
 fastJsonHttpMessageConverter.setSupportedMediaTypes(supportMediaTypeList);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

但是鉴于已经上线的接口,如果在@RequestMapping注解里加入produces 可能会影响到业务调用,如果有业务指定了Accept是text\html就报错了,当然在RequestMapping的produces也可以指定多个MediaType,但是如果还有别的接口也这样的,那就需要修改多个地方,最终还是选择了方案2。上线后确实也解决了报错的问题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/991144
推荐阅读
相关标签
  

闽ICP备14008679号