restemplate請(qǐng)求亂碼之content-encoding=“gzip“示例詳解
什么是 RestTemplate
RestTemplate 是從 Spring3.0 開(kāi)始支持的一個(gè) HTTP 請(qǐng)求工具,它提供了常見(jiàn)的 REST請(qǐng)求方案的模板,例如 GET 請(qǐng)求、POST 請(qǐng)求、PUT 請(qǐng)求、DELETE 請(qǐng)求以及一些通用的請(qǐng)求執(zhí)行方法 exchange 以及 execute。RestTemplate 繼承自 InterceptingHttpAccessor 并且實(shí)現(xiàn)了 RestOperations 接口,其中 RestOptions 接口定義了基本的 RESTful 操作,這些操作在 RestTemplate 中都得到了實(shí)現(xiàn)。
restemplate請(qǐng)求亂碼之content-encoding="gzip"
今天有一個(gè)通過(guò)Restemplate請(qǐng)求一個(gè)天氣API,發(fā)現(xiàn)其Body數(shù)據(jù)是亂碼,同事處理了好久,然并卵,經(jīng)檢查后,發(fā)現(xiàn)頭部信息出了問(wèn)題。
content-encoding="gzip" content-type="application/json;charset=UTF-8"
返回值是UTF-8,Restemplate設(shè)置的也是UTF-8。在翻看其他博客,發(fā)現(xiàn)問(wèn)題原因是http存在一個(gè)壓縮格式:Gzip。
Gzip是一個(gè)壓縮算法,當(dāng)請(qǐng)求數(shù)據(jù)或返回?cái)?shù)據(jù)體積過(guò)大,為減少網(wǎng)絡(luò)負(fù)載壓力而使用的壓縮算法。通常在服務(wù)器端使用,客戶(hù)端為獲得原始數(shù)據(jù)需通過(guò)Gzip解壓。
響應(yīng)頭中的gzip
Content-Encoding
是一個(gè)實(shí)體消息首部,用于對(duì)特定媒體類(lèi)型的數(shù)據(jù)進(jìn)行壓縮。當(dāng)這個(gè)首部出現(xiàn)的時(shí)候,它的值表示消息主體進(jìn)行了何種方式的內(nèi)容編碼轉(zhuǎn)換。這個(gè)消息首部用來(lái)告知客戶(hù)端應(yīng)該怎樣解碼才能獲取在 Content-Type 中標(biāo)示的媒體類(lèi)型內(nèi)容。
一般建議對(duì)數(shù)據(jù)盡可能地進(jìn)行壓縮,因此才有了這個(gè)消息首部的出現(xiàn)。
注:客戶(hù)端和服務(wù)器都可以使用,表示body中的數(shù)據(jù)采用了什么編碼(壓縮算法)
Accept-Encoding
HTTP 請(qǐng)求頭 Accept-Encoding 會(huì)將客戶(hù)端能夠理解的內(nèi)容編碼方式——通常是某種壓縮算法——進(jìn)行通知(給服務(wù)端)。通過(guò)內(nèi)容協(xié)商的方式,服務(wù)端會(huì)選擇一個(gè)客戶(hù)端提議的方式,使用并在響應(yīng)頭 Content-Encoding 中通知客戶(hù)端該選擇。
注:一般是客戶(hù)端使用,表示給服務(wù)器說(shuō)明,客戶(hù)端支持的壓縮算法列表。服務(wù)從中選擇一個(gè)對(duì)響應(yīng)體進(jìn)行壓縮。

/**
* @Title: getClimateByRequst
* @Description: ( 通過(guò)request 獲取天氣信息)
* @Author:lijie
* @since 2021/11/9 14:05
* @Version:1.1.0
* @return: climate:封裝的天氣結(jié)果類(lèi)
*/
public Result<Climate> getClimateByRequst(HttpServletRequest request) {
Result<Climate> result=new Result<>();
try{
//通過(guò)request請(qǐng)求獲得ip地址
String ip = WebUtils.getIpByRequset(request);
//通過(guò)ip獲得大致定位
Result<Map<String, Object>> locationByIP = this.getLocationByIP(ip);
if(locationByIP!=null&&locationByIP.isSuccess()){
Map<String, Object> data = locationByIP.getData();
String url=""http://請(qǐng)求的url地址
if(MapUtils.isNotEmpty(data)){
String jd = MapUtils.getString(data, "jd");//精度
String wd = MapUtils.getString(data, "wd");//緯度
HttpHeaders httpHeaders = new HttpHeaders();
// Accept 表示客戶(hù)端支持什么格式的響應(yīng)體
httpHeaders.set("contentType", "application/json;charset=UTF-8");
// Accept-Encoding 頭,表示客戶(hù)端可以接收gzip格式的壓縮
httpHeaders.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
//發(fā)送請(qǐng)求
ResponseEntity<byte[]> forEntity = restTemplate.exchange
(url, HttpMethod.GET, new HttpEntity<>(httpHeaders), byte[].class);
if(forEntity.getStatusCode()== HttpStatus.OK){
// 獲取服務(wù)器響應(yīng)體編碼
String contentEncoding = forEntity.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
if ("gzip".equals(contentEncoding)) { // 是gzip編碼
// gzip解壓服務(wù)器的Body響應(yīng)體
byte[] weatherData = WebUtils.unGZip(
new ByteArrayInputStream(Objects.requireNonNull(forEntity.getBody())));
String weatherJson = new String(weatherData, StandardCharsets.UTF_8);
Climate climate = JSONObject.parseObject(weatherJson, Climate.class);
if(climate!=null){
result.setSuccess();
result.setData(climate);
}
} else {
// todo 其他的編碼
result.setErrored("和風(fēng)API響應(yīng)值編碼不是Gzip,請(qǐng)聯(lián)系我,謝謝");
}
}else{
result.setErrored("和風(fēng)API響應(yīng)出錯(cuò)了");
}
}
}
}catch (Exception e){
result.setErrored("天氣獲取出錯(cuò)了");
}
return result;
}/**
* Gzip解壓縮
* @param inputStream
* @return
* @throws IOException
*/
public static byte[] unGZip(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
byte[] buf = new byte[4096];
int len = -1;
while ((len = gzipInputStream.read(buf, 0, buf.length)) != -1) {
byteArrayOutputStream.write(buf, 0, len);
}
return byteArrayOutputStream.toByteArray();
} finally {
byteArrayOutputStream.close();
}
}
/**
* Gzip壓縮數(shù)據(jù)
* @param data
* @return
* @throws IOException
*/
public static byte[] gZip(byte[] data) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
gzipOutputStream.write(data);
gzipOutputStream.finish();
return byteArrayOutputStream.toByteArray();
}
}
SpringBoot的響應(yīng)體壓縮配置
實(shí)際上,并不需要自己手動(dòng)去寫(xiě)這種響應(yīng)體的壓縮代碼。springboot提供了相關(guān)的配置。只針對(duì)響應(yīng)壓縮
server:
compression:
# 開(kāi)啟響應(yīng)壓縮
enabled: true
# 支持的壓縮類(lèi)型
mime-types:
- application/json
- application/xml
- application/javascript
- text/html
- text/xml
- text/plain
- text/css
- text/javascript
# 默認(rèn)只有響應(yīng)體大于 2028kb 時(shí)才會(huì)進(jìn)行壓縮
min-response-size: 2048
# 指定不壓縮的user-agent,默認(rèn)為null
# excluded-user-agents對(duì)應(yīng)的配置類(lèi):org.springframework.boot.context.embedded.Compression
最后
使用RestTemplate請(qǐng)求文本數(shù)據(jù)接口,發(fā)現(xiàn)解碼后的字符串是亂碼。此時(shí)除了編碼格式問(wèn)題外就可以懷疑是不是服務(wù)器響應(yīng)了壓縮后的數(shù)據(jù)。解決這個(gè)問(wèn)題,先嘗試移除Accept-Encoding請(qǐng)求頭,告訴服務(wù)器,客戶(hù)端不需要壓縮響應(yīng)體。如果服務(wù)器還是響應(yīng)壓縮后的數(shù)據(jù),嘗試讀取服務(wù)器的Content-Encoding頭,根據(jù)服務(wù)器的壓縮編碼,自己再進(jìn)行解壓縮。
到此這篇關(guān)于restemplate請(qǐng)求亂碼之content-encoding=“gzip“的文章就介紹到這了,更多相關(guān)restemplate請(qǐng)求亂碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合Javamail實(shí)現(xiàn)郵件發(fā)送功能
郵件發(fā)送是一個(gè)很普遍的功能,springboot整合了相關(guān)的starter,本文給大家介紹了可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的郵件發(fā)送功能的實(shí)例,文中通過(guò)代碼給大家介紹的非常詳細(xì),感興趣的朋友可以參考下2023-12-12
SpringBoot系列教程JPA之基礎(chǔ)環(huán)境搭建的方法
這篇文章主要介紹了SpringBoot系列教程JPA之基礎(chǔ)環(huán)境搭建的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
Feign+mybatisplus搭建項(xiàng)目遇到的坑及解決
這篇文章主要介紹了Feign+mybatisplus搭建項(xiàng)目遇到的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
JAVA 根據(jù)設(shè)置的概率生成隨機(jī)數(shù)的方法
本篇文章主要介紹了JAVA 根據(jù)設(shè)置的概率生成隨機(jī)數(shù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
spring啟動(dòng)錯(cuò)誤Singleton bean creation not al
本文主要介紹了spring啟動(dòng)錯(cuò)誤Singleton bean creation not allowed while the singletons of this factory are indestruction,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
springboot集成opencv實(shí)現(xiàn)人臉識(shí)別功能的詳細(xì)步驟
大家都知道OpenCV是一個(gè)基于BSD許可(開(kāi)源)發(fā)行的跨平臺(tái)計(jì)算機(jī)視覺(jué)和機(jī)器學(xué)習(xí)軟件庫(kù),可以運(yùn)行在Linux、Windows、Android和Mac OS操作系統(tǒng)上今天通過(guò)本文給大家分享springboot集成opencv實(shí)現(xiàn)人臉識(shí)別,感興趣的朋友一起看看吧2021-06-06
Eclipse開(kāi)發(fā)JavaWeb項(xiàng)目配置Tomcat的方法步驟
本文主要介紹了Eclipse開(kāi)發(fā)JavaWeb項(xiàng)目配置Tomcat的方法步驟,首先介紹eclipse開(kāi)發(fā)JavaWeb項(xiàng)目需要配置的相關(guān)環(huán)境,使用tomcat軟件在本地搭建服務(wù)器,然后再在eclipse環(huán)境下配置tomcat,感興趣的可以了解一下2021-08-08

