OkHttp学习笔记3--Interceptors

Interceptors

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls. Here's a simple interceptor that logs the outgoing request and the incoming response.

拦截器是一种强大的机制,可以监视、重写和重试calls。下面是一个简单的拦截器,用于记录传出请求和传入响应。

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();

    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

A call to chain.proceed(request) is a critical part of each interceptor’s implementation. This simple-looking method is where all the HTTP work happens, producing a response to satisfy the request.

对chain.proceed(request)的调用是每个拦截器实现的关键部分。这个简单的方法是所有HTTP工作发生的地方,它生成一个响应来满足请求。

Interceptors can be chained. Suppose you have both a compressing interceptor and a checksumming interceptor: you'll need to decide whether data is compressed and then checksummed, or checksummed and then compressed. OkHttp uses lists to track interceptors, and interceptors are called in order.

拦截器可以被链接。假设您同时拥有压缩拦截器和校验和拦截器:您需要决定数据是先压缩后校验和,还是先校验和再压缩。OkHttp使用列表跟踪拦截器,并按顺序调用拦截器。

Interceptors Diagram

Application Interceptors

Interceptors are registered as either application or network interceptors. We'll use the LoggingInterceptor defined above to show the difference.

拦截器注册为应用程序拦截器或网络拦截器。我们将使用上面定义的LoggingInterceptor来显示差异。

Register an application interceptor by calling addInterceptor() on OkHttpClient.Builder:

通过调用OkHttpClient.Builder上的addInterceptor()来注册一个应用程序拦截器:

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();

The URL http://www.publicobject.com/helloworld.txt redirects to https://publicobject.com/helloworld.txt, and OkHttp follows this redirect automatically. Our application interceptor is called once and the response returned from chain.proceed() has the redirected response:

URL http://www.publicobject.com/helloworld.txt重定向到https://publicobject.com/helloworld.txt, OkHttp自动跟随此重定向。我们的应用程序拦截器被调用一次,chain.proceed()返回的响应具有重定向响应:

INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example

INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

We can see that we were redirected because response.request().url() is different from request.url(). The two log statements log two different URLs.

我们可以看到,我们被重定向是因为response.request().url()与request.url()不同。这两个日志语句记录两个不同的url。

Network Interceptors

Registering a network interceptor is quite similar. Call addNetworkInterceptor()instead of addInterceptor():

注册网络拦截器非常类似。调用addNetworkInterceptor()而不是addInterceptor():

OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();

When we run this code, the interceptor runs twice. Once for the initial request to http://www.publicobject.com/helloworld.txt, and another for the redirect to https://publicobject.com/helloworld.txt.

当我们运行这段代码时,拦截器会运行两次。一次是针对http://www.publicobject.com/helloworld.txt的初始请求,另一次是针对重定向到https://publicobject.com/helloworld.txt的请求。

INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}
User-Agent: OkHttp Example
Host: www.publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://publicobject.com/helloworld.txt

INFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}
User-Agent: OkHttp Example
Host: publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

The network requests also contain more data, such as the Accept-Encoding: gzip header added by OkHttp to advertise support for response compression. The network interceptor's Chain has a non-null Connection that can be used to interrogate the IP address and TLS configuration that were used to connect to the webserver.

网络请求还包含更多的数据,例如OkHttp添加的Accept-Encoding: gzip报头,用于宣传对响应压缩的支持。网络拦截器的链有一个非空连接,可用于查询用于连接到web服务器的IP地址和TLS配置。

Choosing between application and network interceptors

在应用程序和网络拦截器之间进行选择

Each interceptor chain has relative merits.

每个拦截器链都有相对的优点。

Application interceptors

应用拦截器

  • Don't need to worry about intermediate responses like redirects and retries.

    不需要担心像重定向和重试这样的中间响应。

  • Are always invoked once, even if the HTTP response is served from the cache.

    总是调用一次,即使从缓存提供HTTP响应也是如此。

  • Observe the application's original intent. Unconcerned with OkHttp-injected headers like If-None-Match.

    观察应用程序的原始意图。不关心okhttp注入的头文件,比如If-None-Match。

  • Permitted to short-circuit and not call Chain.proceed().

    允许短路而不调用Chain.proceed()。

  • Permitted to retry and make multiple calls to Chain.proceed().

    允许重试并多次调用Chain.proceed()。

Network Interceptors

网络拦截器

  • Able to operate on intermediate responses like redirects and retries.

    能够操作中间响应,如重定向和重试。

  • Not invoked for cached responses that short-circuit the network.

    网络短路的缓存响应不会被调用。

  • Observe the data just as it will be transmitted over the network.

    观察将通过网络传输的数据。

  • Access to the Connection that carries the request.

    访问承载请求的连接。

Rewriting Requests

Interceptors can add, remove, or replace request headers. They can also transform the body of those requests that have one. For example, you can use an application interceptor to add request body compression if you're connecting to a webserver known to support it.

拦截器可以添加、删除或替换请求头。它们还可以转换具有一个请求的请求体。例如,如果您连接到已知支持请求体压缩的web服务器,那么可以使用应用程序拦截器来添加请求体压缩。

/** This interceptor compresses the HTTP request body. Many webservers can't handle this! */
final class GzipRequestInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request originalRequest = chain.request();
    if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
      return chain.proceed(originalRequest);
    }

    Request compressedRequest = originalRequest.newBuilder()
        .header("Content-Encoding", "gzip")
        .method(originalRequest.method(), gzip(originalRequest.body()))
        .build();
    return chain.proceed(compressedRequest);
  }

  private RequestBody gzip(final RequestBody body) {
    return new RequestBody() {
      @Override public MediaType contentType() {
        return body.contentType();
      }

      @Override public long contentLength() {
        return -1; // We don't know the compressed length in advance!
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
        body.writeTo(gzipSink);
        gzipSink.close();
      }
    };
  }
}

Rewriting Responses

Symmetrically, interceptors can rewrite response headers and transform the response body. This is generally more dangerous than rewriting request headers because it may violate the webserver's expectations!

对称地,拦截器可以重写响应头并转换响应体。这通常比重写请求头更危险,因为它可能违反web服务器的期望!

If you're in a tricky situation and prepared to deal with the consequences, rewriting response headers is a powerful way to work around problems. For example, you can fix a server's misconfigured Cache-Control response header to enable better response caching:

如果您处于一种棘手的情况,并准备处理其后果,那么重写响应头是一种解决问题的强大方法。例如,您可以修复服务器的错误配置的Cache-Control响应头,以启用更好的响应缓存:

/** Dangerous interceptor that rewrites the server's cache-control header. */
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Response originalResponse = chain.proceed(chain.request());
    return originalResponse.newBuilder()
        .header("Cache-Control", "max-age=60")
        .build();
  }
};

Typically this approach works best when it complements a corresponding fix on the webserver!

通常,这种方法在补充web服务器上的相应修复时工作得最好!

Availability

OkHttp's interceptors require OkHttp 2.2 or better. Unfortunately, interceptors do not work with OkUrlFactory, or the libraries that build on it, including Retrofit ≤ 1.8 and Picasso ≤ 2.4.

OkHttp的拦截器需要OkHttp 2.2或更高版本。不幸的是,拦截器无法工作在,OkUrlFactory,或基于它构建的库,包括 Retrofit ≤ 1.8 and Picasso ≤ 2.4.上。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,165评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,476评论 3 405
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,446评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,157评论 1 301
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,164评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,615评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,969评论 3 430
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,959评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,495评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,529评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,641评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,233评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,976评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,407评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,552评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,218评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,715评论 2 366

推荐阅读更多精彩内容