|
|
@@ -0,0 +1,117 @@
|
|
|
+package space.anyi.httpClient;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.net.URI;
|
|
|
+import java.net.http.HttpClient;
|
|
|
+import java.net.http.HttpRequest;
|
|
|
+import java.net.http.HttpResponse;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 请求头示例:在 HttpRequest 层配置请求头
|
|
|
+ *
|
|
|
+ * <p><b>重要说明:</b>JDK 的 {@link HttpClient.Builder} 并<b>没有</b>提供
|
|
|
+ * 「设置默认请求头」的方法(可通过 javap 查看 Builder 源码确认)。
|
|
|
+ * 因此在开发中,请求头都是在 HttpRequest 构造阶段设置的。</p>
|
|
|
+ *
|
|
|
+ * <p>本示例覆盖三个 API:</p>
|
|
|
+ * <ul>
|
|
|
+ * <li>{@code .header(key, value)}:追加一个请求头</li>
|
|
|
+ * <li>{@code .headers(k1, v1, k2, v2, ...)}:一次追加多组请求头</li>
|
|
|
+ * <li>{@code .setHeader(key, value)}:覆盖同名请求头</li>
|
|
|
+ * </ul>
|
|
|
+ */
|
|
|
+public class HeaderExample {
|
|
|
+
|
|
|
+ /** 服务基地址常量 */
|
|
|
+ private static final String BASE_URL = "http://localhost:8080";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单个请求头:声明响应格式为 JSON。
|
|
|
+ *
|
|
|
+ * @return 已配置 Accept 请求头的请求对象(未发送)
|
|
|
+ */
|
|
|
+ public HttpRequest buildRequestWithSingleHeader() {
|
|
|
+ return HttpRequest.newBuilder()
|
|
|
+ .uri(URI.create(BASE_URL + "/api/users"))
|
|
|
+ .header("Accept", "application/json") // 告诉服务器客户端期望的响应格式
|
|
|
+ .GET()
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 多个请求头:一次追加多组 key-value。
|
|
|
+ *
|
|
|
+ * <p>{@code headers()} 接收「key, value, key, value ...」形式的参数,
|
|
|
+ * 个数必须是偶数。</p>
|
|
|
+ */
|
|
|
+ public HttpRequest buildRequestWithMultipleHeaders() {
|
|
|
+ return HttpRequest.newBuilder()
|
|
|
+ .uri(URI.create(BASE_URL + "/api/users"))
|
|
|
+ .headers(
|
|
|
+ "Content-Type", "application/json", // 请求体格式
|
|
|
+ "Accept", "application/json", // 期望的响应格式
|
|
|
+ "X-Request-Id", "tutorial-001" // 自定义跟踪请求头
|
|
|
+ )
|
|
|
+ .GET()
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 覆盖同名请求头:{@code setHeader()} 会替换已存在的同名头。
|
|
|
+ *
|
|
|
+ * <p>如果先 addHeader 再 setHeader 相同 key,最终只有一个值(即新值)。</p>
|
|
|
+ */
|
|
|
+ public HttpRequest buildRequestWithSetHeader() {
|
|
|
+ return HttpRequest.newBuilder()
|
|
|
+ .uri(URI.create(BASE_URL + "/api/users"))
|
|
|
+ .header("X-Version", "1") // 先设置 1
|
|
|
+ .setHeader("X-Version", "2") // 再覆盖为 2
|
|
|
+ .GET()
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 「Client 级默认请求头」的变通方案。
|
|
|
+ *
|
|
|
+ * <p>既然 HttpClient 本身不能配置默认头,常见的做法是提供一个
|
|
|
+ * 统一的工厂方法,给后续所有通过该方法构造的请求都预置公共请求头,
|
|
|
+ * 达到「同一客户端发出的请求共享默认头」的效果。</p>
|
|
|
+ *
|
|
|
+ * @return 已预置公共请求头(本示例为 Accept 与 User-Agent)的 Builder
|
|
|
+ */
|
|
|
+ public HttpRequest.Builder newBuilderWithDefaultHeaders() {
|
|
|
+ return HttpRequest.newBuilder()
|
|
|
+ // User-Agent 标识客户端身份
|
|
|
+ .header("User-Agent", "JDK-HttpClient-Tutorial/1.0")
|
|
|
+ // Accept 统一声明期望 JSON 响应
|
|
|
+ .header("Accept", "application/json");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送带请求头的 GET 请求,并打印响应头中的 Content-Type,演示响应头读取。
|
|
|
+ *
|
|
|
+ * @return GET /api/users 的响应对象
|
|
|
+ */
|
|
|
+ public HttpResponse<String> sendRequestWithHeaders() throws IOException, InterruptedException {
|
|
|
+ HttpClient httpClient = HttpClient.newHttpClient();
|
|
|
+
|
|
|
+ // 使用工厂方法拿到预置了默认头的 Builder,再追加个性化头
|
|
|
+ HttpRequest request = newBuilderWithDefaultHeaders()
|
|
|
+ .uri(URI.create(BASE_URL + "/api/users"))
|
|
|
+ .GET()
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response =
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ // 读取响应头:headers().firstValue(name) 返回的 Optional<String>
|
|
|
+ // 本例服务端返回 JSON,Content-Type 通常为 application/json
|
|
|
+ String contentType = response.headers()
|
|
|
+ .firstValue("Content-Type")
|
|
|
+ .orElse("unknown");
|
|
|
+
|
|
|
+ System.out.println("响应头 Content-Type: " + contentType);
|
|
|
+
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+}
|