<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Retrofit 是現在最流行的網路開發框架之一,功能十分強大,但是最近確遇到一個十分坑的問題,現在記錄下來,希望看到的人能注意下。
眾所周知,在 HTTP 傳輸時是支援 gzip 壓縮的,使用者端發起請求時在請求頭裡增加 Accept-Encoding: gzip,伺服器端響應時在返回的頭資訊裡增加 Content-Encoding: gzip,這表示傳輸的資料是採用 gzip 壓縮的。預設情況下,傳輸內容是不壓縮的,採用 gzip 壓縮後可以大幅減少傳輸內容大小,這樣可以提高傳輸速度,減少流量的使用。
本來 OkHttp 是預設支援 gzip 解壓縮的,不需要額外設定的。但是我在攔截器裡統一新增了很多請求頭資訊,大概程式碼如下:
public class RequestInterceptor implements Interceptor { public RequestInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Request.Builder builder = chain.request() .newBuilder() .addHeader("Accept", "application/json") .addHeader("Accept-Encoding", "gzip"); Request request = builder.build(); return chain.proceed(request); } }
以前伺服器端沒有開啟 gzip 壓縮,一直都沒有問題,某天突然運維加了 gzip 壓縮,說是為了要省流量頻寬,結果就悲劇了,我們 Android APP 裡所有的介面都報錯了,明明前一秒都是OK的,後一秒就都不能存取了,但是 iOS 裡卻能正常存取,這是最令人崩潰的事情。
立即進行程式碼偵錯,發現 Android 裡的 http 請求返回的都是亂碼字串了,其實這些都是 gzip 壓縮的資料,不是說 OkHttp 是自動支援 gzip 解壓縮的嗎?為什麼我們的返回資料沒有進行 gzip 解壓?還有一個奇怪的現象是,當我把這段程式碼 addHeader("Accept-Encoding", "gzip") 去掉之後,一切又恢復正常了。
這是一個很費解的問題,當我手動加上這個頭資訊時,OkHttp 不會自動解壓 gzip 流,當我去掉時 OkHttp 又會自動解壓 gzip 流了,秉著刨根究底的精神我翻看了原始碼,終於找到了原因。原來 OkHttp 在最終構建請求資訊以及處理返回資訊時,內部使用了一個叫做 BridgeInterceptor 的攔截器,該類的程式碼如下:
public final class BridgeInterceptor implements Interceptor { private final CookieJar cookieJar; public BridgeInterceptor(CookieJar cookieJar) { this.cookieJar = cookieJar; } @Override public Response intercept(Chain chain) throws IOException { Request userRequest = chain.request(); Request.Builder requestBuilder = userRequest.newBuilder(); RequestBody body = userRequest.body(); if (body != null) { MediaType contentType = body.contentType(); //自動增新增請求頭 Content-Type if (contentType != null) { requestBuilder.header("Content-Type", contentType.toString()); } long contentLength = body.contentLength(); //如果傳輸長度不為-1,則表示完整傳輸 if (contentLength != -1) { //設定頭資訊 Content-Length requestBuilder.header("Content-Length", Long.toString(contentLength)); requestBuilder.removeHeader("Transfer-Encoding"); } else { //如果傳輸長度為-1,則表示分塊傳輸,自動設定頭資訊 requestBuilder.header("Transfer-Encoding", "chunked"); requestBuilder.removeHeader("Content-Length"); } } if (userRequest.header("Host") == null) { requestBuilder.header("Host", hostHeader(userRequest.url(), false)); } //如果沒有設定頭資訊 Connection,則自動設定為 Keep-Alive if (userRequest.header("Connection") == null) { requestBuilder.header("Connection", "Keep-Alive"); } // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing // the transfer stream. boolean transparentGzip = false; if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { //如果我們沒有在請求頭資訊裡增加Accept-Encoding,在這裡會自動設定頭資訊 Accept-Encoding = gzip transparentGzip = true; requestBuilder.header("Accept-Encoding", "gzip"); } List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url()); if (!cookies.isEmpty()) { requestBuilder.header("Cookie", cookieHeader(cookies)); } if (userRequest.header("User-Agent") == null) { requestBuilder.header("User-Agent", Version.userAgent()); } Response networkResponse = chain.proceed(requestBuilder.build()); HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); Response.Builder responseBuilder = networkResponse.newBuilder() .request(userRequest); //如果返回的頭資訊裡Content-Encoding = gzip,並且我們沒有手動在請求頭資訊裡設定 Accept-Encoding = gzip,則會進行 gzip 解壓資料流 if (transparentGzip && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) && HttpHeaders.hasBody(networkResponse)) { GzipSource responseBody = new GzipSource(networkResponse.body().source()); Headers strippedHeaders = networkResponse.headers().newBuilder() .removeAll("Content-Encoding") .removeAll("Content-Length") .build(); responseBuilder.headers(strippedHeaders); String contentType = networkResponse.header("Content-Type"); responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody))); } return responseBuilder.build(); } }
上面程式碼關鍵地方我做了註釋,OkHttp會額外的增加很多請求頭資訊,如果我們在程式碼裡沒有手動設定Accept-Encoding = gzip,那麼OkHttp會自動處理gzip的解壓縮;反之,你需要手動對返回的資料流進行gzip解壓縮。
以上就是我的程式碼裡 gzip 處理失敗的根本原因了,更多關於OkHttp接收gzip壓縮資料返回亂碼的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45