1. Overview
1.概述
The Java HttpClient API was introduced with Java 11. The API implements the client-side of the most recent HTTP standards. It supports HTTP/1.1 and HTTP/2, both synchronous and asynchronous programming models.
Java HttpClient API是由Java 11引入的。该API实现了最新的HTTP标准的客户端。它支持HTTP/1.1和HTTP/2,支持同步和异步的编程模型。
We can use it to send HTTP requests and retrieve their responses. Before Java 11, we had to rely on a rudimentary URLConnection implementation or third-party libraries such as Apache HttpClient.
我们可以使用它来发送HTTP请求并检索其响应。在Java 11之前,我们不得不依赖一个简陋的URLConnection实现或第三方库,如Apache HttpClient。
In this tutorial, we’ll look at the sending POST requests using Java HttpClient. We’ll show how to send both synchronous and asynchronous POST requests, as well as concurrent POST requests. In addition, we’ll check how to add authentication parameters and JSON bodies to POST requests.
在本教程中,我们将研究如何使用Java HttpClient发送POST请求。我们将展示如何发送同步和异步的POST请求,以及并发的POST请求。此外,我们将检查如何向POST请求添加认证参数和JSON体。
Finally, we’ll see how to upload files and submit form data. Therefore, we’ll cover most of the common use cases.
最后,我们将看到如何上传文件和提交表单数据。因此,我们将涵盖大多数常见的用例。
2. Preparing a POST Request
2.准备一个POST请求
Before we can send an HTTP request, we’ll first need to create an instance of an HttpClient.
在我们发送一个HTTP请求之前,我们首先需要创建一个HttpClient的实例。
HttpClient instances can be configured and created from its builder using the newBuilder method. Otherwise, if no configuration is required, we can make use of the newHttpClient utility method to create a default client:
HttpClient实例可以通过newBuilder方法从其构建器中进行配置和创建。否则,如果不需要配置,我们可以利用newHttpClient实用方法来创建一个默认客户端。
HttpClient client = HttpClient.newHttpClient();
HttpClient will use HTTP/2 by default. It will also automatically downgrade to HTTP/1.1 if the server doesn’t support HTTP/2.
HttpClient将默认使用HTTP/2。如果服务器不支持HTTP/2,它也会自动降级到HTTP/1.1。
Now we are ready to create an instance of HttpRequest from its builder. We’ll make use of the client instance to send this request later on. The minimum parameters for a POST request are the server URL, request method, and body:
现在我们准备从其构建器中创建一个HttpRequest的实例。我们将在后面利用客户端实例来发送这个请求。一个POST请求的最小参数是服务器URL、请求方法和正文。
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
A request body needs to be supplied via the BodyPublisher class. It’s a reactive-stream publisher that publishes streams of request body on-demand. In our example, we used a body publisher which sends no request body.
一个请求体需要通过BodyPublisher类来提供。这是一个反应式流发布器,按需发布请求体的流。在我们的例子中,我们使用了一个不发送请求体的body publisher。
3. Sending a POST Request
3.发送一个POST请求
Now that we’ve prepared a POST request, let’s look at the different options for sending it.
现在我们已经准备好了一个POST请求,让我们看看发送该请求的不同选项。
3.1. Synchronously
3.1.同步进行
We can send the prepared request using this default send method. This method will block our code until the response has been received:
我们可以使用这个默认的send方法发送准备好的请求。这个方法将阻止我们的代码,直到收到响应。
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString())
The BodyHandlers utility implements various useful handlers, such as handling the response body as a String or streaming the response body to a file. Once the response is received, the HttpResponse object will contain the response status, headers, and body:
BodyHandlers工具实现了各种有用的处理程序,例如将响应体作为String来处理,或者将响应体流式传输到一个文件。一旦收到响应,HttpResponse对象将包含响应状态、头文件和正文。
assertThat(response.statusCode())
.isEqualTo(200);
assertThat(response.body())
.isEqualTo("{\"message\":\"ok\"}");
3.2. Asynchronously
3.2.异步地
We could send the same request from the previous example asynchronously using the sendAsync method. Instead of blocking our code, this method will immediately return a CompletableFuture instance:
我们可以使用sendAsync方法异步发送上一个例子中的相同请求。这个方法不会阻塞我们的代码,而是立即返回一个CompletableFuture instance。
CompletableFuture<HttpResponse<String>> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
The CompletableFuture completes with the HttpResponse once it becomes available:
CompletableFuture在HttpResponse变得可用时,就会完成它。
HttpResponse<String> response = futureResponse.get();
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}");
3.3. Concurrently
3.3.同时进行的
We can combine Streams with CompletableFutures in order to issue several requests concurrently and await their responses:
我们可以将Streams与CompletableFutures相结合,以便同时发出几个请求并等待它们的响应。
List<CompletableFuture<HttpResponse<String>>> completableFutures = serviceUrls.stream()
.map(URI::create)
.map(HttpRequest::newBuilder)
.map(builder -> builder.POST(HttpRequest.BodyPublishers.noBody()))
.map(HttpRequest.Builder::build)
.map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
.collect(Collectors.toList());
Now, let’s wait for all the requests to be complete so that we can process their responses all at once:
现在,让我们等待所有的请求完成,以便我们可以一次处理它们的响应。
CompletableFuture<List<HttpResponse<String>>> combinedFutures = CompletableFuture
.allOf(completableFutures.toArray(new CompletableFuture[0]))
.thenApply(future ->
completableFutures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
As we’ve combined all the responses using the allOf and join methods, we get a new CompletableFuture that holds our responses:
由于我们使用allOf和join方法合并了所有的响应,我们得到了一个新的CompletableFuture,它持有我们的响应。
List<HttpResponse<String>> responses = combinedFutures.get();
responses.forEach((response) -> {
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}");
});
4. Adding Authentication Parameters
4.添加认证参数
We can set an authenticator on the client level for HTTP authentication on all requests:
我们可以在客户端设置一个认证器,对所有请求进行HTTP认证。
HttpClient client = HttpClient.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
"baeldung",
"123456".toCharArray());
}
})
.build();
However, the HttpClient does not send basic credentials until challenged for them with a WWW-Authenticate header from the server.
然而,HttpClient不会发送基本凭证,直到服务器向其提出WWW-Authenticate头的挑战。
To bypass this, we can always create and send the basic authorization header manually:
为了绕过这一点,我们总是可以手动创建和发送基本授权头。
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.noBody())
.header("Authorization", "Basic " +
Base64.getEncoder().encodeToString(("baeldung:123456").getBytes()))
.build();
5. Adding a Body
5.增加一个机构
In the examples so far, we haven’t added any bodies to our POST requests. However, the POST method is commonly used to send data to the server via the request body.
在到目前为止的例子中,我们还没有给POST请求添加任何主体。然而,POST方法通常被用来通过请求体向服务器发送数据。
5.1. JSON Body
5.1 JSON主体
The BodyPublishers utility implements various useful publishers, such as publishing the request body from a String or a file. We can publish JSON data as String, converted using the UTF-8 character set:
BodyPublishers工具实现了各种有用的发布器,例如从String或文件发布请求体。我们可以将JSON数据发布为String,使用UTF-8字符集转换。
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.ofString("{\"action\":\"hello\"}"))
.build();
5.2. Uploading Files
5.2.上传文件
Let’s create a temporary file that we can use for uploading via HttpClient:
让我们创建一个临时文件,我们可以用来通过HttpClient进行上传。
Path file = tempDir.resolve("temp.txt");
List<String> lines = Arrays.asList("1", "2", "3");
Files.write(file, lines);
HttpClient provides a separate method, BodyPublishers.ofFile, for adding a file to the POST body. We can simply add our temporary file as a method parameter, and the API takes care of the rest:
HttpClient提供了一个单独的方法,BodyPublishers.ofFile,用于向POST主体添加文件。我们可以简单地将我们的临时文件作为一个方法参数添加进去,而API则负责其余的工作。
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.ofFile(file))
.build();
5.3. Submitting Forms
5.3.提交表格
Contrary to files, HttpClient does not provide a separate method for posting form data. Therefore, we’ll again need to make use of the BodyPublishers.ofString method:
与文件相反,HttpClient没有为发布表单数据提供一个单独的方法。因此,我们将再次需要利用BodyPublishers.ofString方法。
Map<String, String> formData = new HashMap<>();
formData.put("username", "baeldung");
formData.put("message", "hello");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.ofString(getFormDataAsString(formData)))
.build();
However, we’ll need to convert the form data from a Map to a String using a custom implementation:
然而,我们需要使用一个自定义实现将表单数据从Map转换为String。
private static String getFormDataAsString(Map<String, String> formData) {
StringBuilder formBodyBuilder = new StringBuilder();
for (Map.Entry<String, String> singleEntry : formData.entrySet()) {
if (formBodyBuilder.length() > 0) {
formBodyBuilder.append("&");
}
formBodyBuilder.append(URLEncoder.encode(singleEntry.getKey(), StandardCharsets.UTF_8));
formBodyBuilder.append("=");
formBodyBuilder.append(URLEncoder.encode(singleEntry.getValue(), StandardCharsets.UTF_8));
}
return formBodyBuilder.toString();
}
6. Conclusion
6.结语
In this article, we explored sending POST requests using Java HttpClient API introduced in Java 11.
在这篇文章中,我们探讨了使用Java 11中引入的Java HttpClient API发送POST请求。
We learned how to create an HttpClient instance and prepare a POST request. We saw how to send prepared requests synchronously, asynchronously, and concurrently. Next, we also saw how to add basic authentication parameters.
我们学习了如何创建一个HttpClient实例并准备一个POST请求。我们看到了如何同步地、异步地和并发地发送准备好的请求。接下来,我们还看到了如何添加基本的认证参数。
Finally, we looked at adding a body to a POST request. We covered JSON payloads, uploading files, and submitting form data.
最后,我们研究了在POST请求中添加一个主体。我们涵盖了JSON有效载荷、上传文件和提交表单数据。
As always, the complete source code is available over on GitHub.
一如既往,完整的源代码可在GitHub上获得,。