OkHttp学习笔记2--Connections&Recipes

Connections

Although you provide only the URL, OkHttp plans its connection to your webserver using three types: URL, Address, and Route.

尽管你只提供了URL,OkHttp使用三中方式(URL,地址和路由)来管理到服务器的连接

URLs

URLs (like https://github.com/square/okhttp) are fundamental to HTTP and the Internet. In addition to being a universal, decentralized naming scheme for everything on the web, they also specify how to access web resources.

URLs(如https://github.com/square/okhttp)是HTTP和Internet的基础。除了作为web上所有内容通分散命名方案之外,它还具体说明如何访问web资源

URLs are abstract:

URLs是抽象的

  • They specify that the call may be plaintext (http) or encrypted (https), but not which cryptographic algorithms should be used. Nor do they specify how to verify the peer's certificates (the HostnameVerifier) or which certificates can be trusted (the SSLSocketFactory).

    它们指定调用可以是明文(http)或加密(https),但不指定应该使用哪种加密算法。它们也没有指定如何验证同行的证书(HostnameVerifier)或哪些证书可以信任(SSLSocketFactory)。

  • They don't specify whether a specific proxy server should be used or how to authenticate with that proxy server.

    它们不指定是否应该使用特定的代理服务器,也不指定如何使用该代理服务器进行身份验证。

They're also concrete: each URL identifies a specific path (like /square/okhttp) and query (like ?q=sharks&lang=en). Each webserver hosts many URLs.

它们也是具体的:每个URL标识一个特定的路径(比如/square/okhttp)和查询(比如?q=sharks&lang=en)。每个web服务器承载许多url。

Addresses

Addresses specify a webserver (like github.com) and all of the staticconfiguration necessary to connect to that server: the port number, HTTPS settings, and preferred network protocols (like HTTP/2 or SPDY).

Addresses指定一个web服务器(如github.com)和连接到该服务器所需的所有静态配置:端口号、HTTPS设置和首选网络协议(如HTTP/2或SPDY)。

URLs that share the same address may also share the same underlying TCP socket connection. Sharing a connection has substantial performance benefits: lower latency, higher throughput (due to TCP slow start) and conserved battery. OkHttp uses a ConnectionPool that automatically reuses HTTP/1.x connections and multiplexes HTTP/2 and SPDY connections.

共享相同地址的url也可以共享相同的底层TCP套接字连接。共享连接具有显著的性能优势:更低的延迟、更高的吞吐量(由于TCP启动缓慢)和节约的电池。OkHttp使用自动重用HTTP/1.x的ConnectionPool连接和多路传输HTTP/2和SPDY连接。

In OkHttp some fields of the address come from the URL (scheme, hostname, port) and the rest come from the OkHttpClient.

在OkHttp中,地址的一些字段来自URL(模式、主机名、端口),其余来自OkHttpClient。

Routes

Routes supply the dynamic information necessary to actually connect to a webserver. This is the specific IP address to attempt (as discovered by a DNS query), the exact proxy server to use (if a ProxySelector is in use), and which version of TLS to negotiate (for HTTPS connections).

路由提供实际连接到web服务器所需的动态信息。这是要尝试的特定IP地址(由DNS查询发现)、要使用的确切代理服务器(如果使用了代理选择器)和要协商的TLS版本(对于HTTPS连接)。

There may be many routes for a single address. For example, a webserver that is hosted in multiple datacenters may yield multiple IP addresses in its DNS response.

一个地址可能有多条路由。例如,驻留在多个数据中心的web服务器可能在其DNS响应中产生多个IP地址。

Connections

When you request a URL with OkHttp, here's what it does:

当您使用OkHttp请求URL时,它是这样做的:

  1. It uses the URL and configured OkHttpClient to create an address. This address specifies how we'll connect to the webserver.

    它使用URL和配置的OkHttpClient来创建一个地址。这个地址指定了我们将如何连接到web服务器。

  2. It attempts to retrieve a connection with that address from the connection pool.

    它尝试从连接池中检索具有该地址的连接。

  3. If it doesn't find a connection in the pool, it selects a route to attempt. This usually means making a DNS request to get the server's IP addresses. It then selects a TLS version and proxy server if necessary.

    如果在池中没有找到连接,则选择要尝试的路由。这通常意味着发出DNS请求以获取服务器的IP地址。然后在必要时选择TLS版本和代理服务器。

  4. If it's a new route, it connects by building either a direct socket connection, a TLS tunnel (for HTTPS over an HTTP proxy), or a direct TLS connection. It does TLS handshakes as necessary.

    如果是新路由,则通过构建直接套接字连接、TLS隧道(用于HTTP代理上的HTTPS)或直接TLS连接进行连接。它会在必要的时候握手。

  5. It sends the HTTP request and reads the response.

    它发送HTTP请求并读取响应。

If there's a problem with the connection, OkHttp will select another route and try again. This allows OkHttp to recover when a subset of a server's addresses are unreachable. It's also useful when a pooled connection is stale or if the attempted TLS version is unsupported.

如果连接有问题,OkHttp将选择另一条路由并重试。这允许OkHttp在无法访问服务器地址的子集时进行恢复。当池连接过时或不支持尝试的TLS版本时,它也有用。

Once the response has been received, the connection will be returned to the pool so it can be reused for a future request. Connections are evicted from the pool after a period of inactivity.

一旦接收到响应,连接将返回到池中,以便将来的请求可以重用它。在一段时间不活动之后,将从池中删除连接。

Recipes

We've written some recipes that demonstrate how to solve common problems with OkHttp. Read through them to learn about how everything works together. Cut-and-paste these examples freely; that's what they're for.

我们已经编写了一些例子,演示了如何使用OkHttp解决常见问题。通读它们,了解所有东西是如何一起工作的。自由剪切和粘贴这些示例;这就是它们的用途。

Synchronous Get

同步Get

Download a file, print its headers, and print its response body as a string.

下载文件,打印其头部,并将其响应体打印为字符串。

The string() method on response body is convenient and efficient for small documents. But if the response body is large (greater than 1 MiB), avoid string() because it will load the entire document into memory. In that case, prefer to process the body as a stream.

response body上的string()方法对于小型文档来说既方便又高效。但是如果响应体很大(大于1 MiB),则避免string(),因为它将把整个文档加载到内存中。在这种情况下,更喜欢将主体作为流进行处理。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      Headers responseHeaders = response.headers();
      for (int i = 0; i < responseHeaders.size(); i++) {
        System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
      }

      System.out.println(response.body().string());
    }
  }

Asynchronous Get

异步Get

Download a file on a worker thread, and get called back when the response is readable. The callback is made after the response headers are ready. Reading the response body may still block. OkHttp doesn't currently offer asynchronous APIs to receive a response body in parts.

在工作线程上下载文件,并在响应可读时被回调。回调是在响应头准备好之后进行的。读取响应体仍然可能阻塞。OkHttp目前不提供异步api来接收部分响应体。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        try (ResponseBody responseBody = response.body()) {
          if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

          Headers responseHeaders = response.headers();
          for (int i = 0, size = responseHeaders.size(); i < size; i++) {
            System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
          }

          System.out.println(responseBody.string());
        }
      }
    });
  }

Accessing Headers

Typically HTTP headers work like a Map: each field has one value or none. But some headers permit multiple values, like Guava's Multimap. For example, it's legal and common for an HTTP response to supply multiple Vary headers. OkHttp's APIs attempt to make both cases comfortable.

通常,HTTP报头的工作方式类似于Map:每个字段有一个值或没有值。但是有些header允许多个值,比如Guava的Multimap。例如,HTTP响应提供多个不同的报头是合法且常见的。OkHttp的api试图让这两种情况都变得舒适。

When writing request headers, use header(name, value) to set the only occurrence of name to value. If there are existing values, they will be removed before the new value is added. Use addHeader(name, value) to add a header without removing the headers already present.

在编写请求标头时,使用header(name、value)将唯一出现的名称设置为值。如果存在现有值,将在添加新值之前删除它们。使用addHeader(name,value)添加标题,而不删除已经存在的标题。

When reading response a header, use header(name) to return the last occurrence of the named value. Usually this is also the only occurrence! If no value is present, header(name) will return null. To read all of a field's values as a list, use headers(name).

读取报头响应时,使用header(name)返回命名值的最后一次出现。通常这也是唯一的发生!如果没有值,header(name)将返回null。要将字段的所有值作为列表读取,请使用headers (name)。

To visit all headers, use the Headers class which supports access by index.

要访问所有标题,请使用支持按索引访问的Headers类。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://api.github.com/repos/square/okhttp/issues")
        .header("User-Agent", "OkHttp Headers.java")
        .addHeader("Accept", "application/json; q=0.5")
        .addHeader("Accept", "application/vnd.github.v3+json")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println("Server: " + response.header("Server"));
      System.out.println("Date: " + response.header("Date"));
      System.out.println("Vary: " + response.headers("Vary"));
    }
  }

Posting a String

Use an HTTP POST to send a request body to a service. This example posts a markdown document to a web service that renders markdown as HTML. Because the entire request body is in memory simultaneously, avoid posting large (greater than 1 MiB) documents using this API.

使用HTTP POST向服务发送请求体。这个示例将一个markdown文档发布到一个web服务,该服务将markdown呈现为HTML。由于整个请求体同时位于内存中,因此避免使用此API发布大型(大于1 MiB)文档。

  public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    String postBody = ""
        + "Releases\n"
        + "--------\n"
        + "\n"
        + " * _1.0_ May 6, 2013\n"
        + " * _1.1_ June 15, 2013\n"
        + " * _1.2_ August 11, 2013\n";

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println(response.body().string());
    }
  }

Post Streaming

Here we POST a request body as a stream. The content of this request body is being generated as it's being written. This example streams directly into the Okio buffered sink. Your programs may prefer an OutputStream, which you can get from BufferedSink.outputStream().

在这里,我们将请求体作为流发布。这个请求体的内容是在编写时生成的。这个例子直接流进Okio缓冲接收器。您的程序可能更喜欢OutputStream,您可以从BufferedSink.outputStream()获得它。

  public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    RequestBody requestBody = new RequestBody() {
      @Override public MediaType contentType() {
        return MEDIA_TYPE_MARKDOWN;
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8("Numbers\n");
        sink.writeUtf8("-------\n");
        for (int i = 2; i <= 997; i++) {
          sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
        }
      }

      private String factor(int n) {
        for (int i = 2; i < n; i++) {
          int x = n / i;
          if (x * i == n) return factor(x) + " × " + i;
        }
        return Integer.toString(n);
      }
    };

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(requestBody)
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println(response.body().string());
    }
  }

Posting a File

It's easy to use a file as a request body.

将文件用作请求体很容易。

  public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    File file = new File("README.md");

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println(response.body().string());
    }
  }

Posting form parameters

Use FormBody.Builder to build a request body that works like an HTML

tag. Names and values will be encoded using an HTML-compatible form URL encoding.

使用FormBody。构建器来构建一个类似于HTML 标记的请求体。名称和值将使用html兼容的表单URL编码进行编码。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    RequestBody formBody = new FormBody.Builder()
        .add("search", "Jurassic Park")
        .build();
    Request request = new Request.Builder()
        .url("https://en.wikipedia.org/w/index.php")
        .post(formBody)
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println(response.body().string());
    }
  }

Posting a multipart request

MultipartBody.Builder can build sophisticated request bodies compatible with HTML file upload forms. Each part of a multipart request body is itself a request body, and can define its own headers. If present, these headers should describe the part body, such as its Content-Disposition. The Content-Length and Content-Type headers are added automatically if they're available.

MultipartBody.Builder可以构建与HTML文件上传表单兼容的复杂请求体。多部分请求体的每个部分本身就是一个请求体,可以定义自己的headers。如果存在,这些标题应该描述部件主体,例如Content-Disposition。如果Content-LengthContent-Type 标头可用,它们将自动添加。

  /**
   * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running
   * these examples, please request your own client ID! https://api.imgur.com/oauth2
   */
  private static final String IMGUR_CLIENT_ID = "...";
  private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
    RequestBody requestBody = new MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("title", "Square Logo")
        .addFormDataPart("image", "logo-square.png",
            RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
        .build();

    Request request = new Request.Builder()
        .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
        .url("https://api.imgur.com/3/image")
        .post(requestBody)
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println(response.body().string());
    }
  }

Parse a JSON Response With Moshi

Moshi is a handy API for converting between JSON and Java objects. Here we're using it to decode a JSON response from a GitHub API.

Moshi是一个用于JSON和Java对象之间转换的方便API。这里我们使用它来解码来自GitHub API的JSON响应。

Note that ResponseBody.charStream() uses the Content-Type response header to select which charset to use when decoding the response body. It defaults to UTF-8 if no charset is specified.

注意,ResponseBody.charStream()使用Content-Type的响应头来选择在解码响应体时使用哪个字符集。如果没有指定字符集,则默认为UTF-8。

  private final OkHttpClient client = new OkHttpClient();
  private final Moshi moshi = new Moshi.Builder().build();
  private final JsonAdapter gistJsonAdapter = moshi.adapter(Gist.class);

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://api.github.com/gists/c2a7c39532239ff261be")
        .build();
    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      Gist gist = gistJsonAdapter.fromJson(response.body().source());

      for (Map.Entry entry : gist.files.entrySet()) {
        System.out.println(entry.getKey());
        System.out.println(entry.getValue().content);
      }
    }
  }

  static class Gist {
    Map files;
  }

  static class GistFile {
    String content;
  }

Response Caching

To cache responses, you'll need a cache directory that you can read and write to, and a limit on the cache's size. The cache directory should be private, and untrusted applications should not be able to read its contents!

要缓存响应,您需要一个可以读写的缓存目录,以及对缓存大小的限制。缓存目录应该是私有的,不受信任的应用程序不应该能够读取其内容!

It is an error to have multiple caches accessing the same cache directory simultaneously. Most applications should call new OkHttpClient() exactly once, configure it with their cache, and use that same instance everywhere. Otherwise the two cache instances will stomp on each other, corrupt the response cache, and possibly crash your program.

让多个缓存同时访问同一个缓存目录是一个错误。大多数应用程序应该只调用一次new OkHttpClient(),用它们的缓存配置它,并在任何地方使用相同的实例。否则,这两个缓存实例将相互践踏,破坏响应缓存,并可能导致程序崩溃。

Response caching uses HTTP headers for all configuration. You can add request headers like Cache-Control: max-stale=3600 and OkHttp's cache will honor them. Your webserver configures how long responses are cached with its own response headers, like Cache-Control: max-age=9600. There are cache headers to force a cached response, force a network response, or force the network response to be validated with a conditional GET.

响应缓存对所有配置使用HTTP报头。您可以添加请求头,如Cache-Control: max-stale=3600,OkHttp的缓存将接受它们。您的web服务器配置使用自己的响应头缓存响应的时间,比如Control: max-age=9600。缓存头用于强制缓存响应、强制网络响应或强制使用条件GET验证网络响应。

  private final OkHttpClient client;

  public CacheResponse(File cacheDirectory) throws Exception {
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(cacheDirectory, cacheSize);

    client = new OkHttpClient.Builder()
        .cache(cache)
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    String response1Body;
    try (Response response1 = client.newCall(request).execute()) {
      if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);

      response1Body = response1.body().string();
      System.out.println("Response 1 response:          " + response1);
      System.out.println("Response 1 cache response:    " + response1.cacheResponse());
      System.out.println("Response 1 network response:  " + response1.networkResponse());
    }

    String response2Body;
    try (Response response2 = client.newCall(request).execute()) {
      if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);

      response2Body = response2.body().string();
      System.out.println("Response 2 response:          " + response2);
      System.out.println("Response 2 cache response:    " + response2.cacheResponse());
      System.out.println("Response 2 network response:  " + response2.networkResponse());
    }

    System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
  }

To prevent a response from using the cache, use CacheControl.FORCE_NETWORK
. To prevent it from using the network, use CacheControl.FORCE_CACHE. Be warned: if you use FORCE_CACHEand the response requires the network, OkHttp will return a 504 Unsatisfiable Requestresponse.

要防止响应使用缓存,请使用CacheControl.FORCE_NETWORK。要防止它使用网络,请使用CacheControl.FORCE_CACHE。警告:如果您使用FORCE_CACHE并且响应需要网络,OkHttp将返回一个504无法满足的请求响应。

Canceling a Call

Use Call.cancel() to stop an ongoing call immediately. If a thread is currently writing a request or reading a response, it will receive an IOException. Use this to conserve the network when a call is no longer necessary; for example when your user navigates away from an application. Both synchronous and asynchronous calls can be canceled.

使用Call.cancel()立即停止正在进行的调用。如果线程当前正在编写请求或读取响应,它将接收IOException。当请求不再需要,使用这个功能来节省网络;例如,例如当用户导航离开应用程序时。同步和异步调用都可以取消。

  private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
        .build();

    final long startNanos = System.nanoTime();
    final Call call = client.newCall(request);

    // Schedule a job to cancel the call in 1 second.
    executor.schedule(new Runnable() {
      @Override public void run() {
        System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
        call.cancel();
        System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
      }
    }, 1, TimeUnit.SECONDS);

    System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
    try (Response response = call.execute()) {
      System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
          (System.nanoTime() - startNanos) / 1e9f, response);
    } catch (IOException e) {
      System.out.printf("%.2f Call failed as expected: %s%n",
          (System.nanoTime() - startNanos) / 1e9f, e);
    }
  }

Timeouts

Use timeouts to fail a call when its peer is unreachable. Network partitions can be due to client connectivity problems, server availability problems, or anything between. OkHttp supports connect, read, and write timeouts.

当呼叫的对等方无法到达时,使用超时使呼叫失败。网络断开可能是由于客户端连接问题、服务器可用性问题或两者之间的任何问题造成的。OkHttp支持连接、读取和写入超时。

  private final OkHttpClient client;

  public ConfigureTimeouts() throws Exception {
    client = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
        .build();

    try (Response response = client.newCall(request).execute()) {
      System.out.println("Response completed: " + response);
    }
  }

Per-call Configuration

All the HTTP client configuration lives in OkHttpClient including proxy settings, timeouts, and caches. When you need to change the configuration of a single call, call OkHttpClient.newBuilder(). This returns a builder that shares the same connection pool, dispatcher, and configuration with the original client. In the example below, we make one request with a 500 ms timeout and another with a 3000 ms timeout.

所有的HTTP客户端配置都位于OkHttpClient中,包括代理设置、超时和缓存。当您需要更改单个调用的配置时,请调用OkHttpClient.newBuilder()。这将返回与原始客户端共享相同连接池、分派器和配置的构建器。在下面的示例中,我们发出一个超时为500毫秒的请求,另一个超时为3000毫秒。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
        .build();

    // Copy to customize OkHttp for this request.
    OkHttpClient client1 = client.newBuilder()
        .readTimeout(500, TimeUnit.MILLISECONDS)
        .build();
    try (Response response = client1.newCall(request).execute()) {
      System.out.println("Response 1 succeeded: " + response);
    } catch (IOException e) {
      System.out.println("Response 1 failed: " + e);
    }

    // Copy to customize OkHttp for this request.
    OkHttpClient client2 = client.newBuilder()
        .readTimeout(3000, TimeUnit.MILLISECONDS)
        .build();
    try (Response response = client2.newCall(request).execute()) {
      System.out.println("Response 2 succeeded: " + response);
    } catch (IOException e) {
      System.out.println("Response 2 failed: " + e);
    }
  }

Handling authentication

OkHttp can automatically retry unauthenticated requests. When a response is 401 Not Authorized, an Authenticator is asked to supply credentials. Implementations should build a new request that includes the missing credentials. If no credentials are available, return null to skip the retry.

OkHttp可以自动重试未经身份验证的请求。当响应401未授权时,将要求身份验证器提供凭据。实现应该构建一个包含丢失凭证的新请求。如果没有凭据可用,则返回null以跳过重试。

Use Response.challenges() to get the schemes and realms of any authentication challenges. When fulfilling a Basic challenge, use Credentials.basic(username, password) to encode the request header.

使用Response.challenges()获取任何身份验证挑战的模式和领域。在完成基本的挑战时,使用Credentials.basic(username, password)来编码请求头。

  private final OkHttpClient client;

  public Authenticate() {
    client = new OkHttpClient.Builder()
        .authenticator(new Authenticator() {
          @Override public Request authenticate(Route route, Response response) throws IOException {
            if (response.request().header("Authorization") != null) {
              return null; // Give up, we've already attempted to authenticate.
            }

            System.out.println("Authenticating for response: " + response);
            System.out.println("Challenges: " + response.challenges());
            String credential = Credentials.basic("jesse", "password1");
            return response.request().newBuilder()
                .header("Authorization", credential)
                .build();
          }
        })
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/secrets/hellosecret.txt")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println(response.body().string());
    }
  }

To avoid making many retries when authentication isn't working, you can return null to give up. For example, you may want to skip the retry when these exact credentials have already been attempted:

为了避免身份验证失败时多次重试,可以返回null放弃。例如,您可能希望跳过重试时,这些确切的凭证已经尝试:

  if (credential.equals(response.request().header("Authorization"))) {
    return null; // If we already failed with these credentials, don't retry.
   }

You may also skip the retry when you’ve hit an application-defined attempt limit:

当您达到应用程序定义的尝试限制时,您也可以跳过重试:

  if (responseCount(response) >= 3) {
    return null; // If we've failed 3 times, give up.
  }

This above code relies on this responseCount() method:

上面的代码依赖于responseCount()方法:

  private int responseCount(Response response) {
    int result = 1;
    while ((response = response.priorResponse()) != null) {
      result++;
    }
    return result;
  }

你可能感兴趣的:(OkHttp学习笔记2--Connections&Recipes)