文章目录
  1. 1. 默认不需要设置Accept-Encoding为gzip
  2. 2. 设置请求头信息时,值不能为空

前几天在使用Okhttp时遇到两个坑,这里记录一下。一个是在请求头中不需要设置Accept-Encodinggzip,使用Okhttp默认的就好;第二个是添加到Okhttp请求头里面的键值对不能为空。

默认不需要设置Accept-Encoding为gzip

Okhttp中如果外部请求没有在请求里面设置Accept-Encoding值和Range值时,会在请求头里增加设置Accept-Encoding: gzip,如果外部设置这个Accept-Encoding参数,则不会设置。如果是Okhttp自己设置的参数,并且服务器端响应的头里包含有Content-Encoding值为gzip,则会使用Gzip解压。如果是外部设置的,即使服务器端响应头里面包含有Content-Encoding值为gzip,也不会使用Gzip解压,需要自己去解压。

下面是官方代码,在okhttp3.internal.http.BridgeInterceptor#intercept(Chain chain)方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 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) {
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);

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)));
}

注意整个逻辑是由transparentGzip变量来控制的。刚开始不知道有这种逻辑,使用了Okhttp做请求,然后在请求头里设置了Accept-Encoding值为gzip,于是使用okhttp3.ResponseBody#string()方法来获取服务器端的响应字符串时,结果是乱码。去排查原因时发现上面这段逻辑。

设置请求头信息时,值不能为空

在使用Okhttp请求时,由于设置请求的值为null值,所以App直接闪退,一看错误日志,发现了原因。原来源码里面有拦截操作,具体代码在okhttp3.Headers#checkNameAndValue(String name, String value)方法中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void checkNameAndValue(String name, String value) {
if (name == null) throw new NullPointerException("name == null");
if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
for (int i = 0, length = name.length(); i < length; i++) {
char c = name.charAt(i);
if (c <= '\u0020' || c >= '\u007f') {
throw new IllegalArgumentException(Util.format(
"Unexpected char %#04x at %d in header name: %s", (int) c, i, name));
}
}
if (value == null) throw new NullPointerException("value for name " + name + " == null");
for (int i = 0, length = value.length(); i < length; i++) {
char c = value.charAt(i);
if ((c <= '\u001f' && c != '\t') || c >= '\u007f') {
throw new IllegalArgumentException(Util.format(
"Unexpected char %#04x at %d in %s value: %s", (int) c, i, name, value));
}
}
}

如果添加请求的键或值为null,都会抛出异常,而且还要求键的长度不能为0,值的要求是可见字符,可以是\t。否则都会抛出异常。

解决方法是对请求时添加到头里面的内容过滤下就可以了。

文章目录
  1. 1. 默认不需要设置Accept-Encoding为gzip
  2. 2. 设置请求头信息时,值不能为空