Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens

今天在使用RestTemplate调用接口时发现无法对Gzip的数据进行解压,然后找到了这篇文章: 让RestTemplate使用和解析gzip编码

原因

文章提到RestTemplate默认不使用Gzip,也不解析 Gzip编码结果,这是因为RestTemplate默认SimpleBufferingClientHttpRequest类。通过idea可以看到RestTemplate的继承关系

在HttpAccessor类中,ClientHttpRequestFactory这个private属性的默认值为一个SimpleClientHttpRequestFactory对象

RestTemplate在获取数据时getForObject、getForEntity、postForObject、postForEntity、exchange等方法最终都是调用execute方法

而execute方法会调用doExecute方法,在doExecute方法中我们可以看到这一句

createRequest方法属于HttpAccessor类,这个方法会通过调用getRequestFactory方法获得requestFactory属性,然后调用requestFactory属性的createRequest方法创建一个实现了ClientHttpRequest接口的对象。

通过RestTemplate类的空参构造函数得到的RestTemplate中requestFactory属性为SimpleClientHttpRequestFactory,因此创建一个SimpleBufferingClientHttpRequest对象,而这个对象是不满足我们自动解析Gzip的要求的,这就是出现问题的原因

解决

RestTemplate类提供了另外一个构造函数,指定一个实现了ClientHttpRequestFactory的对象就能够将其注入到HttpAccessor父类的requestFactory属性中

ClientHttpRequestFactory的实现类关系如图:

HttpComponentsClientHttpRequestFactory能够创建HttpComponentsClientHttpRequest对象,而这个对象能够满足我们的需求

要使用这个需要在项目中添加额外的依赖,以gradle为例:

1
implementation 'org.apache.httpcomponents:httpclient:4.5.13'

添加完依赖之后只需要将HttpComponentsClientHttpRequestFactory注入到RestTemplate对象即可

1
2
3
4
5
6
7
@Test
void contextLoads() {
String url = "XXX";
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
ResponseEntity<Qweather> qweatherResponseEntity = restTemplate.getForEntity(url, Qweather.class);
}

运行之后可以看到自动解析了Gzip的数据