1. JDK17 HttpClient 基础概述
JDK17 内置的 HttpClient 是 Java 11 引入的现代化 HTTP 客户端 API,相比传统的 HttpURLConnection 提供了更简洁的 API 设计和更强大的功能支持。这个模块位于 java.net.http 包下,支持 HTTP/2 和 WebSocket,同时向下兼容 HTTP/1.1。
在实际项目中,我们通常需要创建不同类型的 HTTP 请求,包括 GET、POST、PUT、DELETE 等。HttpClient 提供了灵活的构建方式,可以根据不同场景选择最合适的请求创建方法。下面我将详细介绍几种常见的构建方式及其适用场景。
2. HttpClient 的基本使用准备
2.1 创建 HttpClient 实例
在使用 HttpRequest 之前,我们需要先创建一个 HttpClient 实例。HttpClient 是线程安全的,通常一个应用只需要一个实例即可:
java复制HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // 使用HTTP/2协议
.connectTimeout(Duration.ofSeconds(10)) // 设置连接超时
.followRedirects(HttpClient.Redirect.NORMAL) // 设置重定向策略
.build();
这个客户端实例可以重复用于发送多个请求。在实际项目中,建议将 HttpClient 实例作为单例使用,避免频繁创建和销毁带来的性能开销。
2.2 理解 HttpRequest 构建器
HttpRequest 的创建使用了构建器模式(Builder Pattern),通过 HttpRequest.newBuilder() 方法获取构建器实例。这种设计模式使得请求的配置更加灵活和可读:
java复制HttpRequest.Builder builder = HttpRequest.newBuilder();
构建器提供了链式调用的方法,可以方便地设置请求的各种参数。下面我们将具体介绍如何构建不同类型的请求。
3. GET 请求的构建方式
GET 请求是最常用的 HTTP 方法,用于从服务器获取资源。JDK17 HttpClient 提供了多种构建 GET 请求的方式。
3.1 基本 GET 请求
最简单的 GET 请求只需要指定 URI:
java复制HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.GET() // 明确指定GET方法
.build();
注意:虽然 GET 是默认方法,但显式调用 .GET() 方法可以使代码意图更清晰。
3.2 带查询参数的 GET 请求
实际开发中,我们经常需要在 GET 请求中添加查询参数。有几种方式可以实现:
方式一:直接在 URI 中包含查询参数
java复制String url = "https://api.example.com/users?page=1&size=20";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
方式二:使用 URI 构建工具
对于复杂的查询参数,建议使用专门的 URI 构建工具:
java复制URI uri = URI.create("https://api.example.com/users")
.resolve("?page=1&size=20");
HttpRequest request = HttpRequest.newBuilder()
.uri(uri)
.GET()
.build();
3.3 带请求头的 GET 请求
很多 API 需要特定的请求头,如认证信息、内容类型等:
java复制HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/123"))
.header("Authorization", "Bearer abcdef123456")
.header("Accept", "application/json")
.GET()
.build();
提示:对于需要频繁设置的请求头,可以考虑封装成工具方法复用。
4. POST 请求的构建方式
POST 请求通常用于向服务器提交数据,创建新资源。HttpClient 提供了多种方式来构建 POST 请求体。
4.1 发送字符串数据
最简单的 POST 请求可以发送字符串数据:
java复制String requestBody = "{\"name\":\"John\",\"age\":30}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
4.2 发送表单数据
对于传统的表单提交,可以使用以下方式:
java复制String formData = "username=test&password=123456";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/login"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(formData))
.build();
4.3 发送文件内容
HttpClient 支持直接从文件发布内容:
java复制Path filePath = Paths.get("data.json");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/upload"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofFile(filePath))
.build();
4.4 发送字节数组
对于二进制数据,可以使用字节数组:
java复制byte[] imageData = Files.readAllBytes(Paths.get("image.png"));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/images"))
.header("Content-Type", "image/png")
.POST(HttpRequest.BodyPublishers.ofByteArray(imageData))
.build();
5. PUT 请求的构建方式
PUT 请求通常用于更新服务器上的资源。其构建方式与 POST 类似,只是方法不同。
5.1 基本 PUT 请求
java复制String updateData = "{\"name\":\"John Updated\"}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/123"))
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(updateData))
.build();
5.2 无内容的 PUT 请求
某些情况下,PUT 请求可能不需要请求体:
java复制HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/123/activate"))
.PUT(HttpRequest.BodyPublishers.noBody())
.build();
6. DELETE 请求的构建方式
DELETE 请求用于删除服务器上的资源,通常不需要请求体。
6.1 基本 DELETE 请求
java复制HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/123"))
.DELETE()
.build();
6.2 带请求头的 DELETE 请求
某些 API 可能需要认证信息:
java复制HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/123"))
.header("Authorization", "Bearer abcdef123456")
.DELETE()
.build();
7. 其他请求构建技巧
7.1 设置超时时间
可以为请求设置超时时间,避免长时间等待:
java复制HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.timeout(Duration.ofSeconds(30))
.GET()
.build();
7.2 设置期望的响应类型
可以指定期望的响应类型:
java复制HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Accept", "application/json")
.GET()
.build();
7.3 使用代理服务器
如果需要通过代理发送请求:
java复制HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 8080)))
.build();
8. 实际应用中的注意事项
8.1 异常处理
在实际应用中,需要妥善处理各种异常情况:
java复制try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 处理响应
} catch (IOException e) {
// 处理I/O异常
System.err.println("I/O error: " + e.getMessage());
} catch (InterruptedException e) {
// 处理中断异常
Thread.currentThread().interrupt();
System.err.println("Request interrupted: " + e.getMessage());
}
8.2 性能优化
对于高频请求,可以考虑以下优化措施:
- 复用 HttpClient 实例
- 使用连接池(默认已启用)
- 合理设置超时时间
- 考虑异步请求(使用 sendAsync 方法)
8.3 异步请求示例
对于不需要立即结果的请求,可以使用异步方式:
java复制CompletableFuture<HttpResponse<String>> future = client.sendAsync(
request,
HttpResponse.BodyHandlers.ofString()
);
future.thenApply(response -> {
System.out.println("Status code: " + response.statusCode());
return response;
}).exceptionally(e -> {
System.err.println("Request failed: " + e.getMessage());
return null;
});
9. 常见问题解决方案
9.1 如何处理重定向
HttpClient 默认会处理重定向,但可以通过配置修改行为:
java复制HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS) // 总是跟随重定向
.build();
或者完全禁用重定向:
java复制HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER)
.build();
9.2 如何处理 Cookie
如果需要处理 Cookie,可以配置 Cookie 处理器:
java复制HttpClient client = HttpClient.newBuilder()
.cookieHandler(new CookieManager())
.build();
9.3 如何调试请求
可以通过设置日志级别来调试请求:
java复制System.setProperty("jdk.httpclient.HttpClient.log", "all");
或者在创建 HttpClient 时设置日志级别:
java复制HttpClient client = HttpClient.newBuilder()
.executor(Executors.newFixedThreadPool(1))
.build();
10. 综合示例
下面是一个完整的示例,展示了如何使用 HttpClient 发送各种类型的请求并处理响应:
java复制public class HttpClientExample {
private static final HttpClient httpClient = HttpClient.newHttpClient();
public static void main(String[] args) throws Exception {
// GET 请求示例
HttpRequest getRequest = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
.GET()
.build();
HttpResponse<String> getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
System.out.println("GET Response: " + getResponse.body());
// POST 请求示例
String postBody = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";
HttpRequest postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(postBody))
.build();
HttpResponse<String> postResponse = httpClient.send(postRequest, HttpResponse.BodyHandlers.ofString());
System.out.println("POST Response: " + postResponse.body());
// PUT 请求示例
String putBody = "{\"id\":1,\"title\":\"updated title\",\"body\":\"updated body\",\"userId\":1}";
HttpRequest putRequest = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(putBody))
.build();
HttpResponse<String> putResponse = httpClient.send(putRequest, HttpResponse.BodyHandlers.ofString());
System.out.println("PUT Response: " + putResponse.body());
// DELETE 请求示例
HttpRequest deleteRequest = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
.DELETE()
.build();
HttpResponse<String> deleteResponse = httpClient.send(deleteRequest, HttpResponse.BodyHandlers.ofString());
System.out.println("DELETE Status: " + deleteResponse.statusCode());
}
}
在实际开发中,我通常会将这些请求封装成工具类或服务类,以便于复用和维护。例如,可以创建一个 HttpService 类,提供 get、post、put、delete 等方法,统一处理异常和日志记录。
