Spring RestTemplate 调用天气预报接口乱码的解决
Spring RestTemplate 调用天气预报接口可能遇到中文乱码的问题,解决思路如下。
问题出现
我们在网上找了一个免费的天气预报接口 http://wthrcdn.etouch.cn/weather_mini?citykey=101280601。我们希望调用该接口,并将返回的数据解析为 JSON 格式。
核心业务逻辑如下:
private WeatherResponse doGetWeatherData(String uri) { ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class); String strBody = null; if (response.getStatusCodeValue() == 200) { strBody = response.getBody(); } ObjectMapper mapper = new ObjectMapper(); WeatherResponse weather = null; try { weather = mapper.readValue(strBody, WeatherResponse.class); } catch (IOException e) { e.printStackTrace(); } return weather; }
在浏览器里面访问该接口都挺正常。如下图所示:
但在纯 Spring 应用里面,尝试使用 RestTemplate 来调用,结果解析数据为 JSON 失败,因为数据有乱码。如下图所示:
尝试进行编码转换
一开始,我们认为这可能是对方转过来的数据不是 UTF-8 导致的,所以,尝试加入了消息转换器。
@Configuration public class RestConfiguration { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 支持中文编码 return restTemplate; } }
StringHttpMessageConverter 默认是 ISO_8859_1,所以我们设置为了 UTF_8。
再次执行,发现仍然是乱码。
找到问题的根源
这一次我没有再瞎猜了,而是仔细观察了 HTTP 的请求协议。发现消息头里面的蛛丝马迹:
原来,数据是经过 GZIP 压缩过的。默认情况下, RestTemplate 使用的是 JDK 的 HTTP 调用器,并不支持 GZIP 解压,难怪解析不了。
解决方案
既然找到了问题所在,解决起来就简单了。主要考虑了以下几种方案。
1. 编写 GIZP 工具类
处理 Gizp 压缩的数据的工具类如下:
/** * Welcome to https://waylau.com */ package com.waylau.spring.mvc.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; /** * String Util. * * @since 1.0.0 2018年3月27日 * @author <a href="https://waylau.com">Way Lau</a> */ public class StringUtil { /** * 处理 Gizp 压缩的数据. * * @param str * @return * @throws IOException */ public static String conventFromGzip(String str) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in; GZIPInputStream gunzip = null; in = new ByteArrayInputStream(str.getBytes("ISO-8859-1")); gunzip = new GZIPInputStream(in); byte[] buffer = new byte[256]; int n; while ((n = gunzip.read(buffer)) >= 0) { out.write(buffer, 0, n); } return out.toString(); } }
核心业务逻辑如下:
private WeatherResponse doGetWeatherData(String uri) { ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class); String strBody = null; if (response.getStatusCodeValue() == 200) { try { strBody = StringUtil.conventFromGzip(response.getBody()); } catch (IOException e) { e.printStackTrace(); } } ObjectMapper mapper = new ObjectMapper(); WeatherResponse weather = null; try { weather = mapper.readValue(strBody, WeatherResponse.class); } catch (IOException e) { e.printStackTrace(); } return weather; }
2. 使用 Apache HttpClient
使用 Apache HttpClient 作为 REST 客户端。Apache HttpClient 内置了对于 GZIP 的支持
@Configuration public class RestConfiguration { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate( new HttpComponentsClientHttpRequestFactory()); // 使用HttpClient,支持GZIP restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 支持中文编码 return restTemplate; } }
核心业务逻辑如下:
private WeatherResponse doGetWeatherData(String uri) { ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class); String strBody = null; if (response.getStatusCodeValue() == 200) { strBody = response.getBody(); } ObjectMapper mapper = new ObjectMapper(); WeatherResponse weather = null; try { weather = mapper.readValue(strBody, WeatherResponse.class); } catch (IOException e) { e.printStackTrace(); } return weather; }
当然,使用该方案,需要引入 Apache HttpClient 的依赖。
最终效果,完美!
在 Spring Boot 中所使用的差异
也有学员问到,为啥我在“基于Spring Cloud的微服务实战”课程中,没有同样也是使用 RestTemplate, 调用同样的接口,为啥没有出现乱码的问题?
其实,细心的学员应该发现,在课程中,我们同样也是使用了 Apache HttpClient,由于 Spring Cloud 本身也是基于 Spring Boot 来构建的,所以屏蔽了很多消息转换的细节而言。
以下是 Spring Boot 中通过 RestTemplateBuilder 来构建 RestTemplate 的方式:
@Configuration public class RestConfiguration { @Autowired private RestTemplateBuilder builder; @Bean public RestTemplate restTemplate() { return builder.build(); } }
所以学习编码,知其然要知其所以然!
源码
- 本文示例源码,见 “Spring 5 案例大全”(https://github.com/waylau/spring-5-book) 的 “基于 RestTemplate 的天气预报服务”例子
参考引用:
- 《Spring Boot 教程》:https://github.com/waylau/spring-boot-tutorial
- 《基于Spring Boot的博客系统实战》:http://coding.imooc.com/class/125.html
- 原文同步至https://waylau.com/spring-resttemplate-gzip/
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
有了这份人工智能思维导图书单,年薪百万不是梦
点击关注异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书 ——异步小编 1956年,人工智能诞生于IBM在达特茅斯学院(Dartmouth College)举行的一次研讨会上。麦卡锡首次提出了“人工智能”这一术语。它是人工智能历史上的一个重大里程碑。纽厄尔、萧伯纳和西蒙开发了一个叫作“逻辑理论家”的推理程序。它被用于自动定理证明,推动了第一个列表处理语言——信息加工语言的开发。乔姆斯基的生成语法理论影响了自然语言处理。1958年,罗森布拉特发明了感知器。约翰•麦卡锡开发了LISP和人工智能编程语言。 人工智能大潮来了。AlphaGo击败围棋大师李世石后,人工智能的应用仿佛一夜之间遍地开花。在科技潮流的大环境中,现在硅谷的用人单位越来越倾向于雇用既懂理论(思考者)又懂编程(执行者)的工程师。思考者的日常工作是阅读文献以求产生思路,而执行者则是编写代码来实现应用。但是要成为一名真正的工程师,学习机器学习是将思考者和执行者相结合的最快途径。 人工智能的研究包括从数据到知识,从学习到推理。新一代人工智能已经上升为国家战略,其覆盖的范围也...
- 下一篇
java基本数据类型
Java基本数据类型 变量就是申请内存来存储值,也就是说,当创建变量的时候,需要在内存中申请空间。 内存管理系统根据变量类型为变量分配空间,分配空间只能用来存贮该类型数据 因此,通过定义不同类型变量,可以在内存中存储整数,小数或者字符。 Java的两大数据类型; 内置数据类型 应用数据类型 内置数据类型 Java语言提供了八种基本类型,六种字符类型(四个整数类型,两个浮点型),一个字符类型,还有一种布尔型。 byte: byte数据类型是8位,有符号的,以二进制补码表示的整数。 最小值是-128(-2^7); 最大值127(2^7-1); 默认值是0 byte类型用在大型数组中节约空间,主要代替整数,因为byte变量占用的空间只有int类型的四分之一; 例子;byt a =100,byte b = -50 short类型是16位,有符号的以二进制补码表示的整数 int类型是32位,有符号的以二进制补码表示的整数 long数据类型是64位,有符号的以二进制补码表示的整数。 float数据类型是单精度,32位,符号IEEE 754标准的浮点数。 double数据类型是双精度,64位,符合I...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- Mario游戏-低调大师作品
- CentOS关闭SELinux安全模块
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Red5直播服务器,属于Java语言的直播服务器
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池