1. Overview
1.概述
In this tutorial, we’ll explore Java 11’s standardization of HTTP client API that implements HTTP/2 and Web Socket.
在本教程中,我们将探讨Java 11对HTTP客户端API的标准化,该API实现了HTTP/2和Web Socket。
It aims to replace the legacy HttpUrlConnection class that has been present in the JDK since the very early years of Java.
它旨在取代传统的HttpUrlConnection类,该类从Java的早期就已经存在于JDK中。
Until very recently, Java provided only the HttpURLConnection API, which is low-level and isn’t known for being feature-rich and user-friendly.
直到最近,Java只提供了HttpURLConnection API,它是低级别的,并不以功能丰富 和 用户友好而闻名。
Therefore, some widely used third-party libraries were commonly used, such as Apache HttpClient, Jetty and Spring’s RestTemplate.
因此,一些广泛使用的第三方库被普遍使用,例如Apache HttpClient、Jetty和Spring的RestTemplate。
2. Background
2.背景
The change was implemented as a part of JEP 321.
这一变化是作为JEP 321的一部分实施的。
2.1. Major Changes as Part of JEP 321
2.1.作为JEP 321的一部分的主要变化
- The incubated HTTP API from Java 9 is now officially incorporated into the Java SE API. The new HTTP APIs can be found in java.net.HTTP.*
- The newer version of the HTTP protocol is designed to improve the overall performance of sending requests by a client and receiving responses from the server. This is achieved by introducing a number of changes such as stream multiplexing, header compression and push promises.
- As of Java 11, the API is now fully asynchronous (the previous HTTP/1.1 implementation was blocking). Asynchronous calls are implemented using CompletableFuture.The CompletableFuture implementation takes care of applying each stage once the previous one has finished, so this whole flow is asynchronous.
- The new HTTP client API provides a standard way to perform HTTP network operations with support for modern Web features such as HTTP/2, without the need to add third-party dependencies.
- The new APIs provide native support for HTTP 1.1/2 WebSocket. The core classes and interface providing the core functionality include:
- The HttpClient class, java.net.http.HttpClient
- The HttpRequest class, java.net.http.HttpRequest
- The HttpResponse<T> interface, java.net.http.HttpResponse
- The WebSocket interface, java.net.http.WebSocket
2.2. Problems With the Pre-Java 11 HTTP Client
2.2.Java 11之前的HTTP客户端的问题
The existing HttpURLConnection API and its implementation had numerous problems:
现有的HttpURLConnection API及其实现有许多问题。
- URLConnection API was designed with multiple protocols that are now no longer functioning (FTP, gopher, etc.).
- The API predates HTTP/1.1 and is too abstract.
- It works in blocking mode only (i.e., one thread per request/response).
- It is very hard to maintain.
3. HTTP Client API Overview
3.HTTP客户端API概述
Unlike HttpURLConnection, HTTP Client provides synchronous and asynchronous request mechanisms.
与HttpURLConnection不同,HTTP Client提供了同步和异步的请求机制。
The API consists of three core classes:
该API由三个核心类组成。
- HttpRequest represents the request to be sent via the HttpClient.
- HttpClient behaves as a container for configuration information common to multiple requests.
- HttpResponse represents the result of an HttpRequest call.
We’ll examine each of them in more details in the following sections. First, let’s focus on a request.
我们将在下面的章节中更详细地研究它们中的每一个。首先,让我们关注一个请求。
4. HttpRequest
4.HttpRequest
HttpRequest is an object that represents the request we want to send. New instances can be created using HttpRequest.Builder.
HttpRequest是一个代表我们要发送的请求的对象。新的实例可以用HttpRequest.Builder.来创建。
We can get it by calling HttpRequest.newBuilder(). Builder class provides a bunch of methods that we can use to configure our request.
我们可以通过调用HttpRequest.newBuilder()得到它。Builder类提供了一堆方法,我们可以用它们来配置我们的请求。
We’ll cover the most important ones.
我们将介绍最重要的几项。
Note: In JDK 16, there is a new HttpRequest.newBuilder(HttpRequest request, BiPredicate<String,String> filter) method, which creates a Builder whose initial state is copied from an existing HttpRequest.
注意:在JDK 16中,有一个新的HttpRequest.newBuilder(HttpRequest request, BiPredicate<String,String> filter)方法,它可以创建一个Builder,其初始状态是从现有的HttpRequest中复制的。
This builder can be used to build an HttpRequest, equivalent to the original, while allowing amendment of the request state prior to construction, for example, removing headers:
这个构建器可以用来构建一个HttpRequest,等同于原始的,同时允许在构建之前修正请求状态,例如,删除头文件。
HttpRequest.newBuilder(request, (name, value) -> !name.equalsIgnoreCase("Foo-Bar"))
4.1. Setting URI
4.1.设置URI
The first thing we have to do when creating a request is to provide the URL.
创建一个请求时,我们要做的第一件事是提供URL。
We can do that in two ways — using the constructor for Builder with URI parameter or calling method uri(URI) on the Builder instance:
我们可以通过两种方式来实现–使用带有URI参数的Builder构造函数,或者在Builder实例上调用方法uri(URI)。
HttpRequest.newBuilder(new URI("https://postman-echo.com/get"))
HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
The last thing we have to configure to create a basic request is an HTTP method.
为了创建一个基本请求,我们最后要配置的是一个HTTP方法。
4.2. Specifying the HTTP Method
4.2.指定HTTP方法
We can define the HTTP method that our request will use by calling one of the methods from Builder:
我们可以通过调用Builder中的一个方法来定义我们的请求将使用的HTTP方法。
- GET()
- POST(BodyPublisher body)
- PUT(BodyPublisher body)
- DELETE()
We’ll cover BodyPublisher in detail, later.
我们将在后面详细介绍BodyPublisher。
Now let’s just create a very simple GET request example:
现在让我们来创建一个非常简单的GET请求例子。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.GET()
.build();
This request has all parameters required by HttpClient.
该请求具有HttpClient所要求的所有参数。
However, we sometimes need to add additional parameters to our request. Here are some important ones:
然而,我们有时需要向我们的请求添加额外的参数。下面是一些重要的参数。
- The version of the HTTP protocol
- Headers
- A timeout
4.3. Setting HTTP Protocol Version
4.3.设置HTTP协议的版本
The API fully leverages the HTTP/2 protocol and uses it by default, but we can define which version of the protocol we want to use:
该API完全利用HTTP/2协议,并默认使用它,但我们可以定义我们想要使用的协议版本。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.version(HttpClient.Version.HTTP_2)
.GET()
.build();
Important to mention here is that the client will fall back to, e.g., HTTP/1.1 if HTTP/2 isn’t supported.
这里需要提到的是,如果HTTP/2不被支持,客户端将退回到,例如HTTP/1.1。
4.4. Setting Headers
4.4.设置标头
In case we want to add additional headers to our request, we can use the provided builder methods.
如果我们想在我们的请求中添加额外的头信息,我们可以使用所提供的构建器方法。
We can do that by either passing all headers as key-value pairs to the headers() method or by using header() method for the single key-value header:
我们可以通过将所有头像作为键值对传递给headers()方法,或者使用header()方法来处理单个键值的头像。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.headers("key1", "value1", "key2", "value2")
.GET()
.build();
HttpRequest request2 = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.header("key1", "value1")
.header("key2", "value2")
.GET()
.build();
The last useful method we can use to customize our request is a timeout().
我们可以用来定制请求的最后一个有用方法是 timeout()。
4.5. Setting a Timeout
4.5.设置超时
Let’s now define the amount of time we want to wait for a response.
现在我们来定义一下我们要等待响应的时间。
If the set time expires, a HttpTimeoutException will be thrown. The default timeout is set to infinity.
如果设定的时间过期,将抛出一个HttpTimeoutException。默认超时被设置为无穷大。
The timeout can be set with the Duration object by calling method timeout() on the builder instance:
可以通过调用构建器实例上的方法timeout(),用Duration对象设置超时。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.timeout(Duration.of(10, SECONDS))
.GET()
.build();
5. Setting a Request Body
5.设置一个请求主体
We can add a body to a request by using the request builder methods: POST(BodyPublisher body), PUT(BodyPublisher body) and DELETE().
我们可以通过使用请求生成器的方法在请求中添加一个主体。POST(BodyPublisher body), PUT(BodyPublisher body) 和 DELETE()。
The new API provides a number of BodyPublisher implementations out-of-the-box that simplify passing the request body:
新的API提供了一些BodyPublisher的实现,这些实现可以简化请求主体的传递。
- StringProcessor – reads body from a String, created with HttpRequest.BodyPublishers.ofString
- InputStreamProcessor – reads body from an InputStream, created with HttpRequest.BodyPublishers.ofInputStream
- ByteArrayProcessor – reads body from a byte array, created with HttpRequest.BodyPublishers.ofByteArray
- FileProcessor – reads body from a file at the given path, created with HttpRequest.BodyPublishers.ofFile
In case we don’t need a body, we can simply pass in an HttpRequest.BodyPublishers.noBody():
如果我们不需要一个主体,我们可以简单地传入一个HttpRequest.BodyPublishers.noBody()。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/post"))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
Note: In JDK 16, there’s a new HttpRequest.BodyPublishers.concat(BodyPublisher…) method that helps us building a request body from the concatenation of the request bodies published by a sequence of publishers. The request body published by a concatenation publisher is logically equivalent to the request body that would have been published by concatenating all the bytes of each publisher in sequence.
注意:在JDK 16中,有一个新的HttpRequest.BodyPublishers.concat(BodyPublisher…)方法,帮助我们从一连串的发布者发布的请求体的连接中建立一个请求体。由concatenation publisher发布的请求体在逻辑上等同于将每个发布者的所有字节依次串联起来的请求体。
5.1. StringBodyPublisher
5.1.StringBodyPublisher
Setting a request body with any BodyPublishers implementation is very simple and intuitive.
用任何BodyPublishers实现来设置一个请求体是非常简单和直观的。
For example, if we want to pass a simple String as a body, we can use StringBodyPublishers.
例如,如果我们想传递一个简单的String作为主体,我们可以使用StringBodyPublishers。
As we already mentioned, this object can be created with a factory method ofString() — it takes just a String object as an argument and creates a body from it:
正如我们已经提到的,这个对象可以用工厂方法ofString()来创建–它只接受一个String对象作为参数,并从中创建一个主体。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/post"))
.headers("Content-Type", "text/plain;charset=UTF-8")
.POST(HttpRequest.BodyPublishers.ofString("Sample request body"))
.build();
5.2. InputStreamBodyPublisher
5.2.InputStreamBodyPublisher
To do that, the InputStream has to be passed as a Supplier (to make its creation lazy), so it’s a little bit different than StringBodyPublishers.
要做到这一点,InputStream必须作为Supplier被传递(以使其创建变得懒惰),所以它与StringBodyPublishers有点不同。
However, this is also quite straightforward:
然而,这也是很直接的。
byte[] sampleData = "Sample request body".getBytes();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/post"))
.headers("Content-Type", "text/plain;charset=UTF-8")
.POST(HttpRequest.BodyPublishers
.ofInputStream(() -> new ByteArrayInputStream(sampleData)))
.build();
Notice how we used a simple ByteArrayInputStream here. Of course, that can be any InputStream implementation.
注意我们在这里使用了一个简单的ByteArrayInputStream。当然,这可以是任何InputStream实现。
5.3. ByteArrayProcessor
5.3.ByteArrayProcessor
We can also use ByteArrayProcessor and pass an array of bytes as the parameter:
我们也可以使用ByteArrayProcessor并传递一个字节数组作为参数。
byte[] sampleData = "Sample request body".getBytes();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/post"))
.headers("Content-Type", "text/plain;charset=UTF-8")
.POST(HttpRequest.BodyPublishers.ofByteArray(sampleData))
.build();
5.4. FileProcessor
5.4.FileProcessor
To work with a File, we can make use of the provided FileProcessor.
为了处理一个文件,我们可以利用所提供的FileProcessor。
Its factory method takes a path to the file as a parameter and creates a body from the content:
它的工厂方法将文件的路径作为参数,并从内容中创建一个主体。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/post"))
.headers("Content-Type", "text/plain;charset=UTF-8")
.POST(HttpRequest.BodyPublishers.fromFile(
Paths.get("src/test/resources/sample.txt")))
.build();
We’ve covered how to create HttpRequest and how to set additional parameters in it.
我们已经介绍了如何创建HttpRequest以及如何在其中设置附加参数。
Now it’s time to take a deeper look at HttpClient class, which is responsible for sending requests and receiving responses.
现在是时候深入了解一下HttpClient类了,它负责发送请求和接收响应。
6. HttpClient
6.HttpClient
All requests are sent using HttpClient, which can be instantiated using the HttpClient.newBuilder() method or by calling HttpClient.newHttpClient().
所有的请求都是使用HttpClient发送的,它可以使用HttpClient.newBuilder()方法或通过调用HttpClient.newHttpClient()来实例化。
It provides a lot of useful and self-describing methods we can use to handle our request/response.
它提供了很多有用的和自我描述的方法,我们可以用来处理我们的请求/响应。
Let’s cover some of these here.
让我们在这里介绍一下其中的一些情况。
6.1. Handling Response Body
6.1.处理响应主体
Similar to the fluent methods for creating publishers, there are methods dedicated to creating handlers for common body types:
与创建发布者的流畅方法类似,有一些方法专门用于为常见的主体类型创建处理程序。
BodyHandlers.ofByteArray
BodyHandlers.ofString
BodyHandlers.ofFile
BodyHandlers.discarding
BodyHandlers.replacing
BodyHandlers.ofLines
BodyHandlers.fromLineSubscriber
Pay attention to the usage of the new BodyHandlers factrory class.
注意新的BodyHandlers factrory类的用法。
Before Java 11, we had to do something like this:
在Java 11之前,我们必须做这样的事情。
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
And we can now simplify it:
而我们现在可以把它简化。
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
6.2. Setting a Proxy
6.2.设置一个代理
We can define a proxy for the connection by just calling proxy() method on a Builder instance:
我们可以通过在Builder实例上调用proxy()方法,为连接定义一个代理。
HttpResponse<String> response = HttpClient
.newBuilder()
.proxy(ProxySelector.getDefault())
.build()
.send(request, BodyHandlers.ofString());
In our example, we used the default system proxy.
在我们的例子中,我们使用了默认的系统代理。
6.3. Setting the Redirect Policy
6.3.设置重定向策略
Sometimes the page we want to access has moved to a different address.
有时,我们想要访问的页面已经移到了不同的地址。
In that case, we’ll receive HTTP status code 3xx, usually with the information about new URI. HttpClient can redirect the request to the new URI automatically if we set the appropriate redirect policy.
在这种情况下,我们会收到HTTP状态代码3xx,通常会有新URI的信息。HttpClient可以将请求自动重定向到新的URI,如果我们设置了适当的重定向策略。
We can do it with the followRedirects() method on Builder:
我们可以通过Builder上的followRedirects()方法来实现。
HttpResponse<String> response = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.build()
.send(request, BodyHandlers.ofString());
All policies are defined and described in enum HttpClient.Redirect.
所有的策略都在枚举HttpClient.Redirect中定义和描述。
6.4. Setting Authenticator for a Connection
6.4.为一个连接设置认证器
An Authenticator is an object that negotiates credentials (HTTP authentication) for a connection.
一个Authenticator是一个对象,它为一个连接协商凭证(HTTP认证)。
It provides different authentication schemes (such as basic or digest authentication).
它提供不同的认证方案(如基本或摘要认证)。
In most cases, authentication requires username and password to connect to a server.
在大多数情况下,认证需要用户名和密码来连接到服务器。
We can use PasswordAuthentication class, which is just a holder of these values:
我们可以使用PasswordAuthentication类,它只是这些值的一个持有人。
HttpResponse<String> response = HttpClient.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
"username",
"password".toCharArray());
}
}).build()
.send(request, BodyHandlers.ofString());
Here we passed the username and password values as a plaintext. Of course, this would have to be different in a production scenario.
在这里,我们把用户名和密码值作为纯文本传递。当然,在生产情况下,这必须是不同的。
Note that not every request should use the same username and password. The Authenticator class provides a number of getXXX (e.g., getRequestingSite()) methods that can be used to find out what values should be provided.
请注意,不是每个请求都应该使用相同的用户名和密码。Authenticator类提供了一些getXXX(例如,getRequestingSite())方法,可以用来找出应该提供哪些值。
Now we’re going to explore one of the most useful features of new HttpClient — asynchronous calls to the server.
现在我们要探索新HttpClient最有用的功能之一–对服务器的异步调用。
6.5. Send Requests – Sync vs Async
6.5.发送请求–同步与异步
New HttpClient provides two possibilities for sending a request to a server:
新的HttpClient提供了两种向服务器发送请求的可能性。
- send(…) – synchronously (blocks until the response comes)
- sendAsync(…) – asynchronously (doesn’t wait for the response, non-blocking)
Up until now, the send(...) method naturally waits for a response:
到目前为止,send(.…)方法自然是在等待响应。
HttpResponse<String> response = HttpClient.newBuilder()
.build()
.send(request, BodyHandlers.ofString());
This call returns an HttpResponse object, and we’re sure that the next instruction from our application flow will be run only when the response is already here.
这个调用返回一个HttpResponse对象,我们确信,只有当响应已经在这里时,我们的应用流程的下一条指令才会被运行。
However, it has a lot of drawbacks especially when we are processing large amounts of data.
然而,它有很多缺点,特别是在我们处理大量数据的时候。
So, now we can use sendAsync(...) method — which returns CompletableFeature<HttpResponse> — to process a request asynchronously:
因此,现在我们可以使用sendAsync(.…)方法–它返回CompletableFeature<HttpResponse>—来异步处理一个请求。
CompletableFuture<HttpResponse<String>> response = HttpClient.newBuilder()
.build()
.sendAsync(request, HttpResponse.BodyHandlers.ofString());
The new API can also deal with multiple responses, and stream the request and response bodies:
新的API还可以处理多个响应,并流式处理请求和响应体。
List<URI> targets = Arrays.asList(
new URI("https://postman-echo.com/get?foo1=bar1"),
new URI("https://postman-echo.com/get?foo2=bar2"));
HttpClient client = HttpClient.newHttpClient();
List<CompletableFuture<String>> futures = targets.stream()
.map(target -> client
.sendAsync(
HttpRequest.newBuilder(target).GET().build(),
HttpResponse.BodyHandlers.ofString())
.thenApply(response -> response.body()))
.collect(Collectors.toList());
6.6. Setting Executor for Asynchronous Calls
6.6.为异步调用设置Executor
We can also define an Executor that provides threads to be used by asynchronous calls.
我们还可以定义一个Executor,提供异步调用使用的线程。
This way we can, for example, limit the number of threads used for processing requests:
这样,我们可以,例如,限制用于处理请求的线程数量。
ExecutorService executorService = Executors.newFixedThreadPool(2);
CompletableFuture<HttpResponse<String>> response1 = HttpClient.newBuilder()
.executor(executorService)
.build()
.sendAsync(request, HttpResponse.BodyHandlers.ofString());
CompletableFuture<HttpResponse<String>> response2 = HttpClient.newBuilder()
.executor(executorService)
.build()
.sendAsync(request, HttpResponse.BodyHandlers.ofString());
By default, the HttpClient uses executor java.util.concurrent.Executors.newCachedThreadPool().
默认情况下,HttpClient使用执行器java.util.concurrent.Executors.newCachedThreadPool()。
6.7. Defining a CookieHandler
6.7.定义一个CookieHandler
With new API and builder, it’s straightforward to set a CookieHandler for our connection. We can use builder method cookieHandler(CookieHandler cookieHandler) to define client-specific CookieHandler.
有了新的API和构建器,为我们的连接设置一个CookieHandler是很直接的。我们可以使用构建器方法cookieHandler(CookieHandler cookieHandler)来定义客户端特定的CookieHandler。
Let’s define CookieManager (a concrete implementation of CookieHandler that separates the storage of cookies from the policy surrounding accepting and rejecting cookies) that doesn’t allow to accept cookies at all:
让我们定义CookieManager(CookieHandler的具体实现,它将cookie的存储与围绕接受和拒绝cookie的策略分开),它根本不允许接受cookie。
HttpClient.newBuilder()
.cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_NONE))
.build();
In case our CookieManager allows cookies to be stored, we can access them by checking CookieHandler from our HttpClient:
如果我们的CookieManager允许存储cookie,我们可以通过检查CookieHandler从我们的HttpClient访问它们。
((CookieManager) httpClient.cookieHandler().get()).getCookieStore()
Now let’s focus on the last class from Http API — the HttpResponse.
现在让我们关注Http API的最后一个类–HttpResponse。
7. HttpResponse Object
7.HttpResponse 对象
The HttpResponse class represents the response from the server. It provides a number of useful methods, but these are the two most important:
HttpResponse类表示来自服务器的响应。它提供了许多有用的方法,但这是最重要的两个。
- statusCode() returns status code (type int) for a response (HttpURLConnection class contains possible values).
- body() returns a body for a response (return type depends on the response BodyHandler parameter passed to the send() method).
The response object has other useful methods that we’ll cover such as uri(), headers(), trailers() and version().
响应对象还有其他有用的方法,我们将介绍这些方法,如uri()、headers()、trailers()和version()。
7.1. URI of Response Object
7.1.响应对象的URI
The method uri() on the response object returns the URI from which we received the response.
响应对象上的方法uri()返回我们收到响应的URI。
Sometimes it can be different than URI in the request object because a redirection may occur:
有时它可能与请求对象中的URI不同,因为可能发生重定向。
assertThat(request.uri()
.toString(), equalTo("http://stackoverflow.com"));
assertThat(response.uri()
.toString(), equalTo("https://stackoverflow.com/"));
7.2. Headers from Response
7.2.响应中的头信息
We can obtain headers from the response by calling method headers() on a response object:
我们可以通过在响应对象上调用headers()方法,从响应中获得头信息。
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
HttpHeaders responseHeaders = response.headers();
It returns HttpHeaders object, which represents a read-only view of HTTP Headers.
它返回HttpHeaders对象,代表HTTP头信息的只读视图。
It has some useful methods that simplify searching for headers value.
它有一些有用的方法,可以简化搜索页眉值。
7.3. Version of the Response
7.3.答复的版本
The method version() defines which version of HTTP protocol was used to talk with a server.
方法version()定义了与服务器对话所使用的HTTP协议的版本。
Remember that even if we define that we want to use HTTP/2, the server can answer via HTTP/1.1.
请记住,即使我们定义要使用HTTP/2,服务器也可以通过HTTP/1.1来回答。
The version in which the server answered is specified in the response:
服务器回答的版本在响应中被指定。
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.version(HttpClient.Version.HTTP_2)
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
assertThat(response.version(), equalTo(HttpClient.Version.HTTP_1_1));
8. Handling Push Promises in HTTP/2
8.处理HTTP/2中的推送承诺
New HttpClient supports push promises through PushPromiseHandler interface.
新的HttpClient通过PushPromiseHandler接口支持推送许诺。
It allows the server to “push” content to the client additional resources while requesting the primary resource, saving more roundtrip and as a result improves performance in page rendering.
它允许服务器在请求主要资源的同时向客户端 “推送 “内容,以节省更多的往返次数,从而提高页面渲染的性能。
It is really the multiplexing feature of HTTP/2 that allows us to forget about resource bundling. For each resource, the server sends a special request, known as a push promise to the client.
实际上,正是HTTP/2的复用功能让我们忘记了资源捆绑。对于每个资源,服务器都会向客户端发送一个特殊的请求,即所谓的推送承诺。
Push promises received, if any, are handled by the given PushPromiseHandler. A null valued PushPromiseHandler rejects any push promises.
如果收到推送承诺,则由给定的PushPromiseHandler处理。一个空值的PushPromiseHandler会拒绝任何推送承诺。
The HttpClient has an overloaded sendAsync method that allows us to handle such promises, as shown below.
HttpClient有一个重载的sendAsync方法,允许我们处理这种承诺,如下所示。
Let’s first create a PushPromiseHandler:
让我们首先创建一个PushPromiseHandler。
private static PushPromiseHandler<String> pushPromiseHandler() {
return (HttpRequest initiatingRequest,
HttpRequest pushPromiseRequest,
Function<HttpResponse.BodyHandler<String>,
CompletableFuture<HttpResponse<String>>> acceptor) -> {
acceptor.apply(BodyHandlers.ofString())
.thenAccept(resp -> {
System.out.println(" Pushed response: " + resp.uri() + ", headers: " + resp.headers());
});
System.out.println("Promise request: " + pushPromiseRequest.uri());
System.out.println("Promise request: " + pushPromiseRequest.headers());
};
}
Next, let’s use sendAsync method to handle this push promise:
接下来,让我们使用sendAsync方法来处理这个推送承诺。
httpClient.sendAsync(pageRequest, BodyHandlers.ofString(), pushPromiseHandler())
.thenAccept(pageResponse -> {
System.out.println("Page response status code: " + pageResponse.statusCode());
System.out.println("Page response headers: " + pageResponse.headers());
String responseBody = pageResponse.body();
System.out.println(responseBody);
})
.join();
9. Conclusion
9.结论
In this article, we explored the Java 11 HttpClient API that standardized the incubating HttpClient introduced in Java 9 with more powerful changes.
在这篇文章中,我们探讨了Java 11 HttpClient API,该API将Java 9中引入的孵化的HttpClient标准化,并进行了更强大的修改。
The complete code used can be found over on GitHub.
所使用的完整代码可以在GitHub上找到over。
In the examples, we’ve used sample REST endpoints provided by https://postman-echo.com.
在这些例子中,我们使用了https://postman-echo.com提供的REST端点样本。