起因,项目采用的是SpringBoot2.0.4,调用第三方接口时,出现了汉字乱码的情况。
起因
请求代码:
public String getResult(User user) {
//设置请求的content-type
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity<>(user, headers);
ResponseEntity responseEntity = restTemplate.postForEntity(url, entity, String.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
return responseEntity.getBody();
} else {
throw new HomeworkException("调用接口异常,响应码:" + responseEntity.getStatusCode());
}
}
但收到的响应报文中的汉字却是出现了乱码。
经过抓取TCP包TCP抓包分析—以及wireshark工具下载使用发现,在网络传输中响应报文并没有乱码。
那么问题定位到了RestTemplate中的消息转换器,导致中文乱码。
SpringBoot的HttpMessageConverter使用(1)RestTemplate中的应用
对代码加断点调试,由于restTemplate.postForEntity(url, entity, String.class);
第三个参数为String,RestTemplate最终使用了StringHttpMessageConverter
消息转换器进行了转换。
public class StringHttpMessageConverter extends AbstractHttpMessageConverter {
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
private Charset getContentTypeCharset(@Nullable MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
}
else {
Charset charset = getDefaultCharset();
Assert.state(charset != null, "No default charset");
return charset;
}
}
}
由上文看到,当响应报文的content-type中不携带charset
时,默认使用的是ISO_8859_1编码格式。由此导致了响应报文中文乱码。
而经过断点调试,发现第三方接口返回的响应content-type中只是application/json
。而非application/json;charset=UTF-8。导致了中文乱码。
解决
方法1
public UserResp getResult(User user) {
//设置请求的content-type
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity entity = new HttpEntity<>(user, headers);
ResponseEntity responseEntity = restTemplate.postForEntity(url, entity, UserResp.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
return responseEntity.getBody();
} else {
throw new HomeworkException("调用接口异常,响应码:" + responseEntity.getStatusCode());
}
}
restTemplate.postForEntity(url, entity, UserResp.class),当指定具体的对象时,默认会使用MappingJackson2HttpMessageConverter
消息转换器,即可避免由于编码不同导致的乱码问题。
方法2
升级SpringBoot版本,我们另一个项目SpringBoot版本为2.2x,Spring做了优化。
当响应的content-type为json时,默认使用utf-8编码。
方法3
请求报文声明Accept字段:
public String getResult(User user) {
//设置请求的content-type
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
//核心代码
ArrayList accepts = new ArrayList<>();
accepts.add(new MediaType("application","json", Charset.forName("UTF-8")));
headers.setAccept(accepts);
HttpEntity entity = new HttpEntity<>(user, headers);
ResponseEntity responseEntity = restTemplate.postForEntity(url, entity, String.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
return responseEntity.getBody();
} else {
throw new HomeworkException("调用接口异常,响应码:" + responseEntity.getStatusCode());
}
}
请求报文的Accept
字段含义:即希望响应报文的content-type是什么格式。