【原文连接】:https://blog.tecchen.xyz ,博文同步发布到博客园。
因为精力有限,对文章的更新可能不能及时同步,请点击上面的原文连接访问最新内容。
欢迎访问个人我的网站:https://www.tecchen.xyz 。html
在开发扇贝-每日一句时,使用RestTemplate请求扇贝接口,并保存返回的数据。本来正常的代码,通过架构升级后,请求接口时,会返回乱码数据。通过直接访问接口等形式,最终确认是RestTemplate这个bean有问题。
对RestTemplate的声明也比较简单,经过对apache的httpclient进行封装,返回bean实例。代码以下:java
@Bean RestTemplate restTemplate() { return new RestTemplate(httpRequestFactory()); }
经过断点debug发现以String格式接收数据时,底层采用的是StringHttpMessageConverter来处理请求。查看RestTemplate的构造方法以下:apache
public RestTemplate() { this.messageConverters = new ArrayList(); this.errorHandler = new DefaultResponseErrorHandler(); this.uriTemplateHandler = new DefaultUriBuilderFactory(); this.headersExtractor = new RestTemplate.HeadersExtractor(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter(false)); this.messageConverters.add(new SourceHttpMessageConverter()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); if (romePresent) { this.messageConverters.add(new AtomFeedHttpMessageConverter()); this.messageConverters.add(new RssChannelHttpMessageConverter()); } if (jackson2XmlPresent) { this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); } else if (jaxb2Present) { this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } if (jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } else if (gsonPresent) { this.messageConverters.add(new GsonHttpMessageConverter()); } else if (jsonbPresent) { this.messageConverters.add(new JsonbHttpMessageConverter()); } if (jackson2SmilePresent) { this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter()); } if (jackson2CborPresent) { this.messageConverters.add(new MappingJackson2CborHttpMessageConverter()); } }
其中的StringHttpMessageConverter构造方法使用了默认字符集:ISO-8859-1。json
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> { public static final Charset DEFAULT_CHARSET; …… public StringHttpMessageConverter() { this(DEFAULT_CHARSET); } …… static { DEFAULT_CHARSET = StandardCharsets.ISO_8859_1; } }
private final List<HttpMessageConverter<?>> messageConverters
根据RestTemplate的构造方法的源码得知全部的HttpMessageConverter都是放在final List<HttpMessageConverter<?>> messageConverters这个常量集合中。虽然集合不可修改,可是能够对其中的元素StringHttpMessageConverter进行修改。
解决方案思路都是将ISO-8859-1的StringHttpMessageConverter替换为UTF-8的StringHttpMessageConverter。api
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
for (HttpMessageConverter<?> httpMessageConverter : restTemplate.getMessageConverters()) { if (httpMessageConverter instanceof StringHttpMessageConverter) { ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(Charset.forName("UTF-8")); } }
// stream .findFirst(); 也是能够的 Optional<HttpMessageConverter<?>> converter = restTemplate.getMessageConverters().stream().filter(c -> c instanceof StringHttpMessageConverter).findAny(); if(converter.isPresent()) { ((StringHttpMessageConverter) converter.get()).setDefaultCharset(Charset.forName("UTF-8")); }