问题描述
由于系统需要,使用restTemplate调用外部系统提供的接口,接口描述如图所示(已确认使用postman直接调用时可以正常使用):
项目中调用代码如下图所示:
出现参数中文变?乱码问题:
思路与原因探究
既然确定了是restTemplate调用产生的乱码,通过从网上检索的各种资料不难想到是MessageConverter导致的问题,网上使用较多的解决思路主要有以下两种:
- 设置StringHttpMessageConverter的编码为UTF-8,StringHttpMessageConverter的默认编码为ISO-8859-1,即:
如果参数均为String类型,这样设置是有用的,对于我的问题,可以看到在代码中已经进行了设置,但是仍然存在乱码问题 - 在HttpHeader中设置Content-Type的编码为UTF-8,类似如下代码:
其实网上并没有对于multipartFormData设置编码的例子,这里我稍微尝试了一下,不起作用 - 找到引发问题的本质原因
step by step对restTemplate.postForEntity的请求进行追踪:
进入doWithRequest方法的实现查看
断点可以看到,doWithRequest中调用的messageConverter为FormHttpMessageConverter,进入write方法查看
进入writeMultipart方法查看
进入writeParts方法查看
进入writePart方法
查看this.partConverters的值,从而查看StringHttpMessageConverter的编码情况
让我们看一看this.partConverters的初始化赋值情况
查看StringHttpMessageConverter的无参构造函数
由此我们已经找出混合类型参数传输时乱码的原因所在
解决方案
- 利用FormHttpMessageConverter提供的setPartConvetrters方法替换默认的partConverters,然后再替换restTemplate中原先的FormHttpMessageConverter
- 直接修改FormHttpMessageConverter的源码,将编码由ISO改为UTF-8,然后替换jar包
- 修改被调用的接口为纯String类型的参数(修改被调用接口的实现)
这里我采用的解决方案是直接修改了被调用的接口格式,当然这一点建立在可以修改被调用接口的前提下,项目中Spring的版本为 4.2.8,毕竟这个问题可以说是框架代码的bug了,所以我顺便去查了下spring 5.0.10中的实现情况:
尽管StringHttpMessageConverter的默认编码还是ISO,但Spring 5.0.10在FormHttpMessageConverter的构造函数中多了一步applyDefaultCharset的操作,我们来看看这个方法的实现:
从代码上来看,applyDefaultCharset会将converters中的默认编码全部重置为UTF-8,混合类型参数的中文乱码问题已经不复存在,根据网上查阅的资料,这一问题最早在Spring 4.3.0中被修复。 - 所以如果大家遇到这个问题,升级Spring的版本到4.3.0及以上也是可以解决的
总结
混合类型参数的中文乱码问题存在于Spring4.3.0之前的RestTemplate实现中,主要是由于:
- MultipartFormData使用FormHttpMessageConverter来做参数转换
- FormHttpMessageConverter中使用默认无参构造的StringHttpMessageConverter来做String类型的参数转换
- 默认无参构造的StringHttpMessageConverter的默认编码为ISO-8859-1
所以导致了中文乱码问题。实际使用中如果碰到需要特别关注一下这个问题。