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

/**
* @Title: getClimateByRequst
* @Description: ( 通過request 獲取天氣信息)
* @Author:lijie
* @since 2021/11/9 14:05
* @Version:1.1.0
* @return: climate:封裝的天氣結(jié)果類
*/
public Result<Climate> getClimateByRequst(HttpServletRequest request) {
Result<Climate> result=new Result<>();
try{
//通過request請求獲得ip地址
String ip = WebUtils.getIpByRequset(request);
//通過ip獲得大致定位
Result<Map<String, Object>> locationByIP = this.getLocationByIP(ip);
if(locationByIP!=null&&locationByIP.isSuccess()){
Map<String, Object> data = locationByIP.getData();
String url=""http://請求的url地址
if(MapUtils.isNotEmpty(data)){
String jd = MapUtils.getString(data, "jd");//精度
String wd = MapUtils.getString(data, "wd");//緯度
HttpHeaders httpHeaders = new HttpHeaders();
// Accept 表示客戶端支持什么格式的響應(yīng)體
httpHeaders.set("contentType", "application/json;charset=UTF-8");
// Accept-Encoding 頭,表示客戶端可以接收gzip格式的壓縮
httpHeaders.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
//發(fā)送請求
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,請聯(lián)系我,謝謝");
}
}else{
result.setErrored("和風(fēng)API響應(yīng)出錯了");
}
}
}
}catch (Exception e){
result.setErrored("天氣獲取出錯了");
}
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í)際上,并不需要自己手動去寫這種響應(yīng)體的壓縮代碼。springboot提供了相關(guān)的配置。只針對響應(yīng)壓縮
server:
compression:
# 開啟響應(yīng)壓縮
enabled: true
# 支持的壓縮類型
mime-types:
- application/json
- application/xml
- application/javascript
- text/html
- text/xml
- text/plain
- text/css
- text/javascript
# 默認(rèn)只有響應(yīng)體大于 2028kb 時(shí)才會進(jìn)行壓縮
min-response-size: 2048
# 指定不壓縮的user-agent,默認(rèn)為null
# excluded-user-agents對應(yīng)的配置類:org.springframework.boot.context.embedded.Compression
最后
使用RestTemplate請求文本數(shù)據(jù)接口,發(fā)現(xiàn)解碼后的字符串是亂碼。此時(shí)除了編碼格式問題外就可以懷疑是不是服務(wù)器響應(yīng)了壓縮后的數(shù)據(jù)。解決這個問題,先嘗試移除Accept-Encoding請求頭,告訴服務(wù)器,客戶端不需要壓縮響應(yīng)體。如果服務(wù)器還是響應(yīng)壓縮后的數(shù)據(jù),嘗試讀取服務(wù)器的Content-Encoding頭,根據(jù)服務(wù)器的壓縮編碼,自己再進(jìn)行解壓縮。
到此這篇關(guān)于restemplate請求亂碼之content-encoding=“gzip“的文章就介紹到這了,更多相關(guān)restemplate請求亂碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Eclipse中Properties和yml配置文件注釋亂碼的解決
這篇文章主要介紹了Eclipse中Properties和yml配置文件注釋亂碼的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
通過Interface掃描獲取所有實(shí)現(xiàn)類方式
文章介紹了如何使用ClassPathScanningCandidateComponentProvider從指定包中掃描接口實(shí)現(xiàn)類,并通過接口統(tǒng)一管理和獲取實(shí)現(xiàn)對象,這種方法避免了手動創(chuàng)建對象和修改代碼的問題,并且可以處理鏈路方式下的多處理器順序處理2025-11-11
SpringBoot整合RabbitMQ之路由模式的實(shí)現(xiàn)
本文主要介紹了SpringBoot整合RabbitMQ之路由模式的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-06-06
springcloud連接遠(yuǎn)程nacos失敗顯示localhost服務(wù)連接失敗的問題解決
這篇文章主要介紹了springcloud連接遠(yuǎn)程nacos失敗顯示localhost服務(wù)連接失敗的問題解決,文中有詳細(xì)的代碼示例供大家參考,對大家解決問題有一定的幫助,需要的朋友可以參考下2024-03-03
SpringBoot定制化Starter實(shí)現(xiàn)方法
小伙伴們曾經(jīng)可能都經(jīng)歷過整天寫著CURD的業(yè)務(wù),都沒寫過一些組件相關(guān)的東西,這篇文章記錄一下SpringBoot如何自定義一個Starter。原理和理論就不用多說了,可以在網(wǎng)上找到很多關(guān)于該方面的資料,這里主要分享如何自定義2023-01-01

