java accept encoding_Accept-Encoding gzip 乱码 和Okhttp的解决方法

在使用okhttp请求服务器数据的时候,发现返回的数据一直都是乱码,但是使用fiddler抓包,decode后,可以正常显示。刚开始一直怀疑是编码的问题,后来对比了hex的数据和程序中乱码的二进制,发现不一样。fiddler可以自动处理,说明不是密钥加密。经过不断的尝试发现时Accept-Encoding设置的问题,下面就一一详细介绍一下

Accept-Encoding

设置Accept-Encoding为gzip,deflate,返回的网页是乱码

HTTP 内容协商

要了解 Vary 的作用,先得了解 HTTP 的内容协商机制。有时候,同一个 URL 可以提供多份不同的文档,这就要求服务端和客户端之间有一个选择最合适版本的机制,这就是内容协商。

协商方式有两种

一种是服务端把文档可用版本列表发给客户端让用户选,这可以使用 300 Multiple Choices 状态码来实现。这种方案有不少问题,首先多一次网络往返;其次服务端同一文档的某些版本可能是为拥有某些技术特征的客户端准备的,而普通用户不一定了解这些细节。举个例子,服务端通常可以将静态资源输出为压缩和未压缩两个版本,压缩版显然是为支持压缩的客户端而准备的,但如果让普通用户选,很可能选择错误的版本。

另外一种方案:服务端根据客户端发送的请求头中某些字段自动发送最合适的版本。可以用于这个机制的请求头字段又分两种:内容协商专用字段(Accept 字段)、其他字段。HTTP 的内容协商通常使用这个方案

请求头字段

说明

响应头字段

Accept

告知服务器发送何种媒体类型

Content-Type

Accept-Language

告知服务器发送何种语言

Content-Language

Accept-Charset

告知服务器发送何种字符集

Content-Type

Accept-Encoding

告知服务器采用何种压缩方式

Content-Encoding

例如客户端发送以下请求头:

Accept:*/*

Accept-Encoding:gzip,deflate,sdch

Accept-Language:zh-CN,en-US;q=0.8,en;q=0.6

表示它可以接受任何 MIME 类型的资源;支持采用 gzip、deflate 或 sdch 压缩过的资源;可以接受 zh-CN、en-US 和 en 三种语言,并且 zh-CN 的权重最高(q 取值 0 - 1,最高为 1,最低为 0,默认为 1),服务端应该优先返回语言等于 zh-CN 的版本。

浏览器的响应头可能是这样的:

Content-Type: text/javascript

Content-Encoding: gzip

表示这个文档确切的 MIME 类型是 text/javascript;文档内容进行了 gzip 压缩;响应头没有 Content-Language 字段,通常说明返回版本的语言正好是请求头 Accept-Language 中权重最高的那个。

有时候,上面四个 Accept 字段并不够用,例如要针对特定浏览器如 IE6 输出不一样的内容,就需要用到请求头中的 User-Agent 字段。类似的,请求头中的 Cookie 也可能被服务端用做输出差异化内容的依据。

由于客户端和服务端之间可能存在一个或多个中间实体(如缓存服务器),而缓存服务最基本的要求是给用户返回正确的文档。如果服务端根据不同 User-Agent 返回不同内容,而缓存服务器把 IE6 用户的响应缓存下来,并返回给使用其他浏览器的用户,肯定会出问题 。

所以 HTTP 协议规定,如果服务端提供的内容取决于 User-Agent 这样「常规 Accept 协商字段之外」的请求头字段,那么响应头中必须包含 Vary 字段,且 Vary 的内容必须包含 User-Agent。同理,如果服务端同时使用请求头中 User-Agent 和 Cookie 这两个字段来生成内容,那么响应中的 Vary 字段看上去应该是这样的:

Vary: User-Agent, Cookie

也就是说 Vary 字段用于列出一个响应字段列表,告诉缓存服务器遇到同一个 URL 对应着不同版本文档的情况时,如何缓存和筛选合适的版本。

在Okhttp中的解决方法

上面返回乱码的原因就是没有正确解压Gzip的数据,

分析:

在Okhttp中,如果在请求头添加addHeader("Accept-Encoding", "gzip, deflate"),Okhttp不会帮你处理Gzip的解压,需要你自己去处理。

部分源码如下:

boolean transparentGzip = false;

if (userRequest.header("Accept-Encoding") == null)

{

transparentGzip = true;

requestBuilder.header("Accept-Encoding", "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);

responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));

}

解决方法:

把Request的header中去掉header(“Accept-Encoding”)就行了

有朋友可能想到,如果在header添加"Accept-Encoding", "gzip, deflate",返回的是gzip压缩的数据,自己解压可不可以?

使用 其他的http库,可以拿到最原始的http数据,是可以的。但使用okhttp,返回的字符串是经过编码的,此时已经不是gzip数据格式了,所以没法解压

参考:

Java使用GZIP进行压缩和解压缩(GZIPOutputStream,GZIPInputStream)

Okhttp使用总结

【已解决】设置Accept-Encoding为gzip,deflate,返回的网页是乱码

httpclient Accept-Encoding 乱码

关注我的公众号,轻松了解和学习更多技术

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"br" 是一种较新的 HTTP 压缩算法,也称为 Brotli 压缩算法。在 Java 中,可以使用第三方库 okhttp 来解析 "br" 编码的响应体。 以下是一个示例代码: ```java import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.internal.http.HttpHeaders; import okhttp3.internal.http.RealResponseBody; import okhttp3.internal.http1.Http1Codec; import java.io.IOException; import java.util.ArrayList; public class BrotliResponseDecoder { public static void main(String[] args) throws IOException { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://example.com") .addHeader("Accept-Encoding", "br") .build(); Response response = client.newCall(request).execute(); ResponseBody responseBody = response.body(); Headers headers = response.headers(); MediaType contentType = responseBody.contentType(); if (HttpHeaders.hasBody(response)) { ArrayList<ResponseBody> intermediates = new ArrayList<>(); ResponseBody intermediateBody = new RealResponseBody(contentType.toString(), -1L, intermediates); Http1Codec http1Codec = new Http1Codec(null, null, null, null, -1L, false); intermediates.add(responseBody); responseBody = http1Codec.openResponseBody(intermediateBody); } String decodedResponse = responseBody.string(); System.out.println(decodedResponse); } } ``` 在这个示例代码中,我们通过 OkHttpClient 发送了一个带有 "Accept-Encoding: br" 请求头部的 GET 请求。然后,我们可以通过调用 Response 对象的 body() 方法获取响应体 ResponseBody 对象,并通过 headers() 方法获取响应头 Headers 对象。接着,我们判断响应体是否存在,如果存在则调用 openResponseBody() 方法对响应体进行解压缩,最后通过 string() 方法获取解压后的响应体字符串。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值