Java HTTP Client (Java 11+)

Java 11 引入了新的 HttpClient API,提供現代化的 HTTP 請求功能,支援同步和非同步操作、HTTP/2 等。

引入套件

import java.net.http.*;
import java.net.URI;

建立 HttpClient

// 使用預設設定
HttpClient client = HttpClient.newHttpClient();

// 自訂設定
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .followRedirects(HttpClient.Redirect.NORMAL)
    .connectTimeout(Duration.ofSeconds(10))
    .build();

GET 請求

同步請求

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .GET()
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println("狀態碼: " + response.statusCode());
System.out.println("回應: " + response.body());

非同步請求

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .build();

client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body)
    .thenAccept(System.out::println)
    .join();

POST 請求

// JSON 資料
String json = """
    {
        "name": "Alice",
        "email": "alice@example.com"
    }
    """;

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(json))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

設定請求標頭

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/data"))
    .header("Authorization", "Bearer " + token)
    .header("Accept", "application/json")
    .header("User-Agent", "Java HttpClient")
    .build();

其他 HTTP 方法

// PUT
HttpRequest put = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users/1"))
    .PUT(HttpRequest.BodyPublishers.ofString(json))
    .build();

// DELETE
HttpRequest delete = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users/1"))
    .DELETE()
    .build();

// PATCH(自訂方法)
HttpRequest patch = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users/1"))
    .method("PATCH", HttpRequest.BodyPublishers.ofString(json))
    .build();

處理回應

不同的 BodyHandler

// 字串
HttpResponse<String> strResponse = client.send(request, HttpResponse.BodyHandlers.ofString());

// 位元組陣列
HttpResponse<byte[]> bytesResponse = client.send(request, HttpResponse.BodyHandlers.ofByteArray());

// 串流
HttpResponse<InputStream> streamResponse = client.send(request, HttpResponse.BodyHandlers.ofInputStream());

// 寫入檔案
HttpResponse<Path> fileResponse = client.send(request, 
    HttpResponse.BodyHandlers.ofFile(Path.of("output.txt")));

// 捨棄(只關心狀態碼)
HttpResponse<Void> discardResponse = client.send(request, HttpResponse.BodyHandlers.discarding());

取得回應資訊

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

int statusCode = response.statusCode();
String body = response.body();
HttpHeaders headers = response.headers();
URI uri = response.uri();

// 取得特定標頭
Optional<String> contentType = headers.firstValue("Content-Type");
List<String> cookies = headers.allValues("Set-Cookie");

超時設定

HttpClient client = HttpClient.newBuilder()
    .connectTimeout(Duration.ofSeconds(10))
    .build();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/data"))
    .timeout(Duration.ofSeconds(30))  // 請求超時
    .build();

表單提交

String formData = "username=alice&password=secret";

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://example.com/login"))
    .header("Content-Type", "application/x-www-form-urlencoded")
    .POST(HttpRequest.BodyPublishers.ofString(formData))
    .build();

實用範例

REST API 封裝

public class ApiClient {
    private final HttpClient client;
    private final String baseUrl;
    
    public ApiClient(String baseUrl) {
        this.baseUrl = baseUrl;
        this.client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();
    }
    
    public String get(String path) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + path))
            .GET()
            .build();
        
        return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
    }
    
    public String post(String path, String json) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + path))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(json))
            .build();
        
        return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
    }
}

// 使用
ApiClient api = new ApiClient("https://api.example.com");
String users = api.get("/users");
String result = api.post("/users", """{"name":"Alice"}""");

並行請求

HttpClient client = HttpClient.newHttpClient();

List<URI> uris = List.of(
    URI.create("https://api.example.com/user/1"),
    URI.create("https://api.example.com/user/2"),
    URI.create("https://api.example.com/user/3")
);

List<CompletableFuture<String>> futures = uris.stream()
    .map(uri -> HttpRequest.newBuilder(uri).build())
    .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
    .map(future -> future.thenApply(HttpResponse::body))
    .collect(Collectors.toList());

// 等待全部完成
List<String> results = futures.stream()
    .map(CompletableFuture::join)
    .collect(Collectors.toList());

重點整理

  • Java 11+ 的 HttpClient 提供現代化 HTTP API
  • 支援同步(send)和非同步(sendAsync)請求
  • 支援 HTTP/2 和 WebSocket
  • 使用 Builder 模式設定請求和客戶端
  • BodyHandlers 提供多種回應處理方式
  • 搭配 CompletableFuture 進行並行請求