背景:
我使用的是grails框架,里面集成了spring 全家桶,我需要与另一个系统交互文件以及描述文件的文本内容。使用的RestTemplate进行交互。
问题描述:
restTemplate中使用multipart/form-data 类型的时候,远程服务器接收到的中文是乱码:【???】,在网上也看了很多文章,一直没有解决。
思路与原因探究
既然确定了是restTemplate调用产生的乱码,通过从网上检索的各种资料不难想到是MessageConverter导致的问题,网上使用较多的解决思路主要有以下两种:
设置StringHttpMessageConverter的编码为UTF-8,StringHttpMessageConverter的默认编码为ISO-8859-1,即:
如果参数均为String类型,这样设置是有用的,对于我的问题,可以看到在代码中已经进行了设置,但是仍然存在乱码问题
在HttpHeader中设置Content-Type的编码为UTF-8,类似如下代码:
其实网上并没有对于multipartFormData设置编码的例子,这里我稍微尝试了一下,不起作用
找到引发问题的本质原因
用debug 断点step by step对restTemplate.postForEntity的请求进行追踪:
进入doWithRequest方法的实现查看
进入writeParts方法查看
进入writePart方法
查看this.partConverters的值,从而查看StringHttpMessageConverter的编码情况
我们回过头看一看this.partConverters的初始化赋值情况,可以看到他默认给我们调用的是StringHttpMessageConverter()无参构造方法,我们可以点进去看看无参构造方法正好有个默认编码就是ISO-8859-1;
查看StringHttpMessageConverter的无参构造函数
由此我们已经找出混合类型参数传输时乱码的原因所在。
解决方案
利用FormHttpMessageConverter提供的setPartConvetrters方法替换默认的partConverters,然后再替换restTemplate中原先的FormHttpMessageConverter,这里需要我们自动构建一下RestTemplate一下是创建代码: 在自己创建converters 时一定要把
new AllEncompassingFormHttpMessageConverter()注释调,这个方法是会处理mutipart表单类型的数据的,而且选择转换器的时候是优先选择我们自己创建的FormHttpMessageConerter放在了后面就会用不到,因为优先使用原则。
private static RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplate()
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory()
requestFactory.setConnectTimeout(5000) // 连接超时时间(毫秒)
requestFactory.setReadTimeout(5000) // 读取超时时间(毫秒)
restTemplate.setRequestFactory(requestFactory)
List<HttpMessageConverter<?>> converters = new ArrayList<>()
converters.add(new ByteArrayHttpMessageConverter())
converters.add(new ResourceHttpMessageConverter())
converters.add(new SourceHttpMessageConverter<Source>())
// converters.add(new AllEncompassingFormHttpMessageConverter())
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8))
FormHttpMessageConverter formConverter = new FormHttpMessageConverter()
formConverter.setPartConverters(converters)
formConverter.setCharset(StandardCharsets.UTF_8)
converters.add(formConverter)
restTemplate.setMessageConverters(converters)
return restTemplate
}
以下是接口调用时代码:
static def sendReport(String token, LinkedMultiValueMap form) {
def headers = new HttpHeaders()
headers.setContentType(new MediaType("multipart", "form-data", StandardCharsets.UTF_8))
headers.set("Authorization", "Bearer " + token)
def restTemplate = createRestTemplate()
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(form, headers)
try {
// ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost:8080/pmlis/pathologyOrders/testUpload2Image", entity, String.class)
ResponseEntity<String> responseEntity = restTemplate.postForEntity(Util.IMAGE_MANAGEMENT_PLATFORM_SENDREPORT_URL, entity, String.class)
if (responseEntity.getStatusCode().is2xxSuccessful()) {
return true
} else {
String responseBody = responseEntity.getBody()
def jsonSlurper = new JsonSlurper()
def result = jsonSlurper.parseText(responseBody)
throw new RuntimeException("Send report fail ${result}")
// 处理失败情况
// 根据 result.code 和 result.msg 进行相应的错误处理
}
} catch (Exception e) {
throw new RuntimeException("send a request fail ,that cuase:${e}")
}
}