1. 概述
Java 11 引入了Java HttpClient API,实现了最新HTTP标准的客户端部分。它支持HTTP/1.1和HTTP/2,提供了同步和异步编程模型。
我们可以使用它发送HTTP请求并获取响应。在Java 11之前,我们不得不依赖原始的URLConnection实现,或第三方库如Apache HttpClient。
在这个教程中,我们将学习如何使用Java HttpClient发送POST请求。我们将展示如何发送同步、异步以及并发POST请求,并查看如何添加身份验证参数和JSON。最后,我们将了解如何上传文件和提交表单数据,从而覆盖大部分常见用例。
2. 准备POST请求
在发送HTTP请求之前,我们需要先创建一个HttpClient实例。
HttpClient
实例可以通过其构建器进行配置和创建,使用newBuilder
方法。如果没有特殊配置需求,我们可以使用newHttpClient
实用方法创建一个默认客户端:
HttpClient client = HttpClient.newHttpClient();
HttpClient
默认使用HTTP/2,如果服务器不支持HTTP/2,它会自动降级到HTTP/1.1。
现在,我们准备好使用构建器创建一个HttpRequest
实例。稍后我们会使用这个客户端实例发送请求。POST请求的最小参数包括服务器URL、请求方法和主体:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
请求主体需要通过BodyPublisher
类提供。这是一个响应式流发布者,按需发布请求体的流。在我们的示例中,我们使用了一个不发送请求主体的body发布者。
3. 发送POST请求
现在我们已经准备好了POST请求,让我们看看发送它的不同方式。
3.1. 同步请求
我们可以使用默认的send
方法发送准备好的请求。这个方法将阻塞代码直到收到响应:
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString())
BodyHandlers
实用工具实现了各种有用的处理器,例如将响应体作为String
处理或将响应体流式写入文件。一旦收到响应,HttpResponse
对象将包含响应状态、头和体:
assertThat(response.statusCode())
.isEqualTo(200);
assertThat(response.body())
.isEqualTo("{\"message\":\"ok\"}");
3.2. 异步请求
我们可以使用sendAsync
方法异步发送上一个示例中的相同请求。与同步方法不同,这个方法会立即返回一个CompletableFuture
实例:
CompletableFuture<HttpResponse<String>> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
一旦CompletableFuture
可用,它将完成并返回HttpResponse
:
HttpResponse<String> response = futureResponse.get();
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}");
3.3. 并发请求
我们可以结合Stream和CompletableFuture
来 并发发送多个请求并等待它们的响应:
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());
现在,让我们等待所有请求完成,以便一次处理它们的响应:
CompletableFuture<List<HttpResponse<String>>> combinedFutures = CompletableFuture
.allOf(completableFutures.toArray(new CompletableFuture[0]))
.thenApply(future ->
completableFutures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
由于我们使用了allOf
和join
方法组合所有响应,我们得到了一个新的CompletableFuture
,其中包含了我们的响应:
List<HttpResponse<String>> responses = combinedFutures.get();
responses.forEach((response) -> {
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}");
});
4. 添加身份验证参数
我们可以在客户端级别设置一个身份验证器,以便在所有请求中进行HTTP身份验证:
HttpClient client = HttpClient.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
"baeldung",
"123456".toCharArray());
}
})
.build();
然而,HttpClient
不会在服务器发送带有WWW-Authenticate
头的挑战时自动发送基本凭据。
为了绕过这个问题,我们可以始终手动创建并发送Authorization请求头:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.noBody())
.header("Authorization", "Basic " +
Base64.getEncoder().encodeToString(("baeldung:123456").getBytes()))
.build();
5. 添加请求体
5.1. JSON请求
使用 BodyPublishers
工具类可以很方便的发送各种数据类型,从String
类型到文件上传。我们可以将JSON数据作为字符串类型发送:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.ofString("{\"action\":\"hello\"}"))
.build();
5.2. 上传文件
让我们创建一个临时文件,以便通过HttpClient
上传:
Path file = tempDir.resolve("temp.txt");
List<String> lines = Arrays.asList("1", "2", "3");
Files.write(file, lines);
HttpClient
提供了单独的方法BodyPublishers.ofFile
,用于将文件添加到POST请求体中:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.ofFile(file))
.build();
5.3. Form表单
与文件不同,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))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(getFormDataAsString(formData)))
.build();
但是,我们需要将表单数据从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. 总结
在这篇文章中,我们探讨了Java 11中引入的Java HttpClient API发送POST请求的方法。
我们学习了如何创建HttpClient
实例和准备POST请求。我们看到了如何同步、异步和并发发送准备好的请求。接着,我们也了解了如何添加基本身份验证参数。
最后,我们研究了如何向POST请求添加主体。我们涵盖了JSON负载、文件上传和表单提交。
如往常一样,完整的源代码可在GitHub上找到。