Fork me on Gitee

RestTemplate--解决中文乱码

在开发扇贝-每日一句时,使用RestTemplate请求扇贝接口,并保存返回的数据。原本正常的代码,经过架构升级后,请求接口时,会返回乱码数据。经过直接访问接口等形式,最终确认是RestTemplate这个bean有问题。
对RestTemplate的声明也比较简单,通过对apache的httpclient进行封装,返回bean实例。代码如下:

1
2
3
4
@Bean
RestTemplate restTemplate() {
return new RestTemplate(httpRequestFactory());
}

通过断点debug发现以String格式接收数据时,底层采用的是StringHttpMessageConverter来处理请求。查看RestTemplate的构造方法如下:

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
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。

1
2
3
4
5
6
7
8
9
10
11
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> messageConverters这个常量集合中。虽然集合不可修改,但是可以对其中的元素StringHttpMessageConverter进行修改。
解决方案思路都是将ISO-8859-1的StringHttpMessageConverter替换为UTF-8的StringHttpMessageConverter。

  • 示例代码一

    1
    restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
  • 示例代码二

    1
    2
    3
    4
    5
    for (HttpMessageConverter<?> httpMessageConverter : restTemplate.getMessageConverters()) {
    if (httpMessageConverter instanceof StringHttpMessageConverter) {
    ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8);
    }
    }
  • 示例代码三

    1
    2
    3
    4
    5
    // stream .findFirst(); 也是可以的
    Optional<HttpMessageConverter<?>> converter = restTemplate.getMessageConverters().stream().filter(c -> c instanceof StringHttpMessageConverter).findAny();
    if(converter.isPresent()) {
    ((StringHttpMessageConverter) converter.get()).setDefaultCharset(StandardCharsets.UTF_8);
    }
感谢您发财的小手,我们一起进步
TEC-CHEN 微信支付

微信支付

TEC-CHEN 支付宝

支付宝

TEC-CHEN 微信-赞赏码

微信-赞赏码

TEC-CHEN 公众号

公众号


-------------本文结束感谢您的阅读-------------
0%