1. 概述
Java HttpClient API是在Java 11中引入的。该API实现了最新HTTP标准的客户端。它支持HTTP/1.1和HTTP/2,同步和异步编程模型。
我们可以使用它来发送HTTP请求并检索它们的响应。在Java 11之前,我们不得不依赖基本的URLConnection实现或第三方库,例如Apache HttpClient。
在本教程中,我们将介绍使用Java HttpClient发送POST请求。我们将展示如何发送同步和异步POST请求,以及并发POST请求。此外,我们将检查如何向POST请求添加身份验证参数和JSON主体。
最后,我们将看到如何上传文件和提交表单数据。因此,我们将涵盖大多数常见用例。
2. 准备POST请求
在发送HTTP请求之前,我们首先需要创建一个HttpClient实例。
可以使用newBuilder方法从其构建器配置和创建HttpClient实例。否则,如果不需要配置,我们可以使用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类提供。它是一个响应流发布者,可以按需发布请求主体流。在我们的示例中,我们使用了一个不发送请求主体的主体发布者。
3. 发送POST请求
现在我们已经准备好POST请求,让我们看看发送它的不同选项。
3.1 同步
我们可以使用此默认send方法发送准备好的请求。此方法将阻塞我们的代码,直到收到响应为止:
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString())
BodyHandlers实用程序实现各种有用的处理程序,例如将响应主体作为字符串处理或将响应主体流式传输到文件。收到响应后,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与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());
现在,让我们等待所有请求完成,以便我们可以一次处理所有响应:
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(
"tuyucheng",
"123456".toCharArray());
}
})
.build();
但是,HttpClient不会发送基本凭据,直到使用来自服务器的WWW-Authenticate标头对它们进行质询。
要绕过这一点,我们始终可以手动创建和发送基本授权标头:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.noBody())
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString(("tuyucheng:123456").getBytes()))
.build();
5. 添加正文
到目前为止,在示例中,我们还没有向POST请求添加任何正文。但是,POST方法通常用于通过请求体向服务器发送数据。
5.1 JSON正文
BodyPublishers实用程序实现了各种有用的发布者,例如从字符串或文件发布请求正文。我们可以将JSON数据发布为String,使用UTF-8字符集转换:
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主体。我们可以简单地将我们的临时文件添加为方法参数,API会处理剩下的事情:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.POST(HttpRequest.BodyPublishers.ofFile(file))
.build();
5.3 提交表单
与文件相反,HttpClient不提供用于发布表单数据的单独方法。因此,我们将再次需要使用BodyPublishers.ofString方法:
Map<String, String> formData = new HashMap<>();
formData.put("username", "tuyucheng");
formData.put("message", "hello");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serviceUrl))
.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上获得。