文章目录
- 1.问题描述
- 2.问题分析
- 3.问题解决
- 3.1 Apache HttpClient 依赖
- 3.2 RestTemplate 配置类
- 3.3 测试
1.问题描述
直接通过浏览器访问请求没有问题,但是通过 RestTemplate 访问请求却会出现乱码问题。
2.问题分析
首先我认为是 SpringBoot 版本、JDK 版本、项目结构配置的问题,但是即使我进行更新也没有效果。
然后再想的是默认注入的 RestTemplate 是不是内置的字符编码有问题。
于是我 Debug 查看 RestTemplate 的配置流程。
而该请求响应的 content-type
为 application/json;charset=utf-8
因此,我猜测是字符编码的问题,于是我需要重新设置一下默认的字符编码。
/*** 通过 RestTemplate,我们可以发出 http 请求(支持 Restful 风格),* 去调用 Controller 提供的 API 接口, 就像我们使用浏览器发出 http 请求, 调用该 API 接口一样** @author 狐狸半面添* @create 2024-03-29 0:30*/
@Configuration
public class RestTemplateConfig {/*** 配置注入 RestTemplate bean/对象** @return 实例对象*/@Beanpublic RestTemplate restTemplate() {RestTemplate restTemplate = new RestTemplate();// 对所有 RestTemplate 的消息转换器进行遍历restTemplate.getMessageConverters().forEach(httpMessageConverter -> {// 找到 httpMessageConverter,修改默认的字符编码if (httpMessageConverter instanceof StringHttpMessageConverter) {((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(java.nio.charset.StandardCharsets.UTF_8);}});return restTemplate;}
}
但遗憾的是,还是没有用。再结合我之前使用 RestTemplate 的经历,也没有去设置过 DefaultCharset
,因此我觉得应该本来就不需要设置,RestTemplate 在获取到响应数据后会解析到 content-type
中的 charset=utf-8
从而按照声明的 utf-8 字符集进行解码处理。
在最后呢,我结合着浏览器可以正常响应数据而 RestTemplate 不可以,出现了乱码,以及我观察到响应头的一个字段 Content-Encoding: gzip
。因此我猜测是不是请求 URL 对应的服务器已经对数据进行了 gzip 压缩而导致的 RestTemplate 显示乱码。至于浏览器为什么能正常解析,这是因为浏览器已经对响应数据做了处理才进行的显示,浏览器发现 Content-Encoding: gzip
就对数据进行了解压缩。
那 RestTemplate 如何处理经过 gzip 压缩后的响应数据呢?那我们就需要借助 Apache HttpClient 的功能来处理 Gzip 压缩的响应。
原理:当 RestTemplate 接收到经过 Gzip 压缩的响应时,底层的 HTTP 客户端库会自动解压缩这个响应,然后将解压缩后的内容传递给 RestTemplate 进行处理。
3.问题解决
3.1 Apache HttpClient 依赖
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version>
</dependency>
3.2 RestTemplate 配置类
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;/*** 通过 RestTemplate,我们可以发出 http 请求(支持 Restful 风格),* 去调用 Controller 提供的 API 接口, 就像我们使用浏览器发出 http 请求, 调用该 API 接口一样** @author 狐狸半面添* @create 2023-02-17 1:43*/
@Configuration
public class RestTemplateConfig {/*** 配置注入 RestTemplate bean/对象** @return 实例对象*/@Beanpublic RestTemplate restTemplate() {// 加入 Apache HttpClient 的功能HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());RestTemplate restTemplate = new RestTemplate(factory);// 这里也可以不注释// restTemplate.getMessageConverters().forEach(httpMessageConverter -> {// if (httpMessageConverter instanceof StringHttpMessageConverter) {// ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(java.nio.charset.StandardCharsets.UTF_8);// }// });return restTemplate;}
}
3.3 测试
@RestController
public class WeatherController {/*** 这里你需要根据自己的业务进行配置*/private final static String apiKey = "**************************************";/*** 访问风和天气 url 的访问格式* {} 表示占位符*/private final static String URL = "https://geoapi.qweather.com/v2/city/lookup?location={cityName}&key={apiKey}";@Resourceprivate RestTemplate restTemplate;/*** 根据用户输入的城市查询城市天气* 请求示例:http://localhost:8080/city/长沙** @param cityName* @return*/@GetMapping("/city/{cityName}")public String getWeatherByCityName(@PathVariable("cityName") String cityName) {// 方式一// String url = URL + cityName + "&key=" + apiKey;// ResponseEntity<String> responseEntity = restTemplate.getForEntity(URL, String.class);// 方式二// public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)// 相应数据格式是 application/json; charset=utf-8// 如果你需要做额外数据处理,可以先将响应的 json 数据转为 bean 做进一步处理// 例如:JSON.parseObject(responseEntity.getBody(), Class<T> objectClass)ResponseEntity<String> responseEntity = restTemplate.getForEntity(URL, String.class, cityName, apiKey);return responseEntity.getBody();}}