OkHttp-Interceptors拦截器

来源:互联网 发布:双轨制软件开发公司 编辑:程序博客网 时间:2024/05/29 18:02

基本用例

传送门

GET A URL

This program downloads a URL and print its contents as a string. Full source.

OkHttpClient client = new OkHttpClient();String run(String url) throws IOException {  Request request = new Request.Builder()      .url(url)      .build();  Response response = client.newCall(request).execute();  return response.body().string();}

POST TO A SERVER
This program posts data to a service. Full source.

public static final MediaType JSON    = MediaType.parse("application/json; charset=utf-8");OkHttpClient client = new OkHttpClient();String post(String url, String json) throws IOException {  RequestBody body = RequestBody.create(JSON, json);  Request request = new Request.Builder()      .url(url)      .post(body)      .build();  Response response = client.newCall(request).execute();  return response.body().string();}

OkHttp之 Interceptors 拦截器

拦截器在OkHttp中占据重要作用,在Http请求前进行拦截处理,形成一个链,其源码后续会进行分析,本节只关注用法。

拦截器的接口类定义如下:

public interface Interceptor {  Response intercept(Chain chain) throws IOException;  interface Chain {    Request request();    Response proceed(Request request) throws IOException;    /**     * Returns the connection the request will be executed on. This is only available in the chains     * of network interceptors; for application interceptors this is always null.     */    @Nullable Connection connection();  }}

如果自定义拦截器,需要实现这个接口intercept(Chain chain)方法,下面自定义一个log拦截器。

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

拦截器可以多个链接.假设您有一个压缩拦截器和校验拦截器:你需要决定数据是先压缩然后校验,还是先校验后压缩.OkHttp使用列表追踪拦截器,拦截器按顺序被调用。

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调用 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();

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 nullUser-Agent: OkHttp ExampleINFO: Received response for https://publicobject.com/helloworld.txt in 1179.7msServer: nginx/1.4.6 (Ubuntu)Content-Type: text/plainContent-Length: 1759Connection: keep-alive

我们可以看到调用被重定向了,因为 response.request().url() 不同于 request.url(). 两个日志语句打印出两个不同的url.

Network Interceptors 网络拦截器

注册一个网络拦截器和上面非常相似. 调用 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();

当我们运行这段代码时,拦截器运行两次.第一次是初始化请求到 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 ExampleHost: www.publicobject.comConnection: Keep-AliveAccept-Encoding: gzipINFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6msServer: nginx/1.4.6 (Ubuntu)Content-Type: text/htmlContent-Length: 193Connection: keep-aliveLocation: https://publicobject.com/helloworld.txtINFO: 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 ExampleHost: publicobject.comConnection: Keep-AliveAccept-Encoding: gzipINFO: Received response for https://publicobject.com/helloworld.txt in 80.9msServer: nginx/1.4.6 (Ubuntu)Content-Type: text/plainContent-Length: 1759Connection: keep-alive

网络请求也包含更多的数据,如 Accept-Encoding: gzip 头信息, OkHttp添加该头信息来通知并支持响应的压缩。网络拦截器链有一个非空连接,可用于查询用于连接到网络服务器的IP地址和TLS配置。

应用拦截器

  1. 不需要担心中间过程的响应,如重定向和重试.
  2. 总是只调用一次,即使HTTP响应是从缓存中获取.
  3. 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
  4. 允许短路而不调用 Chain.proceed(),即中止调用.
  5. 允许重试,使 Chain.proceed()调用多次.

网络拦截器

  1. 能够操作中间过程的响应,如重定向和重试.
  2. 当网络短路而返回缓存响应时不被调用.
  3. 只观察在网络上传输的数据.
  4. 携带请求来访问连接.

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.

拦截器可以添加、删除或替换请求头信息.他们还可以改变的请求携带的实体.例如, 如果你连接到一个支持压缩的网络服务器你可以使用一个应用拦截器来添加请求实体压缩.

/** 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!

与重写请求对称,拦截器可以重写响应头信息和改变响应实体.这通常比重写请求头信息更加危险,因为它可能违反网络服务器的期望!

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. *//** 重写服务器 cache-control 头信息的拦截器是危险的. */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!

通常这种方法最好实现在相应的网络服务器上!

0 0
原创粉丝点击