首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 發送 HTTP 請求完整指南:HttpURLConnection、HttpClient、OkHttp 三大方案比較

     編輯:Java 相關知識

前言

Java 發送 HTTP 請求有三大主流方案:JDK 內建 HttpURLConnection (舊)、JDK 11+ HttpClient (現代標準)、OkHttp (業界事實標準)。本文提供統一介面 HttpRequester 封裝 HttpURLConnection 的經典實作、完整回應物件 HttpResponse、並對比三方案優缺點與遷移建議。

1. 統一回應物件 HttpResponse

package com.example.http;

import java.util.List;

public final class HttpResponse {
    private final int statusCode;
    private final String reasonPhrase;
    private final String body;
    private final String contentType;
    private final String contentEncoding;
    private final List<String> headers; // 簡化:僅儲存原始 Header 行
    private final String requestUrl;
    private final String method;

    // 建構子、Getter 省略...
    public boolean isSuccess() { return statusCode >= 200 && statusCode < 300; }
}

2. HttpRequester 封裝 HttpURLConnection (JDK 8+ 相容)

package com.example.http;

import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class HttpRequester {
    private final String defaultCharset;
    private final int connectTimeout;
    private final int readTimeout;

    public HttpRequester() {
        this(StandardCharsets.UTF_8.name(), 5000, 15000);
    }

    public HttpRequester(String defaultCharset, int connectTimeout, int readTimeout) {
        this.defaultCharset = defaultCharset;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
    }

    // ---- 便利方法 ----
    public HttpResponse get(String url) throws IOException {
        return send(url, "GET", null, null);
    }
    public HttpResponse get(String url, Map<String, String> params) throws IOException {
        return send(buildUrlWithParams(url, params), "GET", null, null);
    }
    public HttpResponse post(String url, String body) throws IOException {
        return send(url, "POST", body, Map.of("Content-Type", "application/json"));
    }
    public HttpResponse postForm(String url, Map<String, String> form) throws IOException {
        return send(url, "POST", buildFormBody(form), Map.of("Content-Type", "application/x-www-form-urlencoded"));
    }

    // ---- 核心送出邏輯 ----
    private HttpResponse send(String url, String method, String body, Map<String, String> headers) throws IOException {
        HttpURLConnection conn = null;
        try {
            URL urlObj = new URL(url);
            conn = (HttpURLConnection) urlObj.openConnection();
            conn.setRequestMethod(method);
            conn.setConnectTimeout(connectTimeout);
            conn.setReadTimeout(readTimeout);
            conn.setUseCaches(false);
            conn.setDoInput(true);

            if (headers != null) {
                headers.forEach(conn::setRequestProperty);
            }

            if ("POST".equalsIgnoreCase(method) && body != null) {
                conn.setDoOutput(true);
                try (OutputStream os = conn.getOutputStream()) {
                    os.write(body.getBytes(defaultCharset));
                }
            }

            int status = conn.getResponseCode();
            String encoding = conn.getContentEncoding();
            if (encoding == null) encoding = defaultCharset;

            InputStream is = (status >= 400) ? conn.getErrorStream() : conn.getInputStream();
            String respBody;
            try (BufferedReader br = new BufferedReader(new InputStreamReader(is, encoding))) {
                respBody = br.lines().collect(Collectors.joining("\n"));
            }

            List<String> headerLines = new ArrayList<>();
            conn.getHeaderFields().forEach((k, v) -> {
                if (k != null) v.forEach(val -> headerLines.add(k + ": " + val));
            });

            return new HttpResponse(status, conn.getResponseMessage(), respBody,
                    conn.getContentType(), encoding, headerLines, url, method);
        } finally {
            if (conn != null) conn.disconnect();
        }
    }

    private String buildUrlWithParams(String url, Map<String, String> params) {
        if (params == null || params.isEmpty()) return url;
        StringBuilder sb = new StringBuilder(url);
        sb.append(url.contains("?") ? "&" : "?");
        params.forEach((k, v) -> sb.append(URLEncoder.encode(k, StandardCharsets.UTF_8))
                .append("=").append(URLEncoder.encode(v, StandardCharsets.UTF_8)).append("&"));
        sb.setLength(sb.length() - 1);
        return sb.toString();
    }

    private String buildFormBody(Map<String, String> form) {
        return form.entrySet().stream()
                .map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "=" +
                        URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
                .collect(Collectors.joining("&"));
    }
}

3. 使用範例

HttpRequester client = new HttpRequester();
HttpResponse resp = client.get("https://httpbin.org/get", Map.of("q", "java"));
if (resp.isSuccess()) {
    System.out.println(resp.getBody());
} else {
    System.err.println("Error: " + resp.getStatusCode() + " " + resp.getReasonPhrase());
}

4. 三大方案深度比較

維度HttpURLConnectionJDK HttpClient (11+)OkHttp 4.x
API 風格同步阻塞、冗長同步/非同步、Builder、函數式同步/非同步、Builder、攔截器鏈
HTTP/2 支援無內建內建 (含 HTTP/3 實驗)
連線池簡單全域池內建、可配置強大、可配置、支援連線遷移
超時控制連線/讀取分離連線/請求/閒置全支援連線/讀取/寫入/呼叫全支援
重試機制無無 (需 Resilience4j)內建可配置重試、備援路徑
攔截器/日誌無無 (需封裝)強大攔截器鏈 (簽名、日誌、壓縮)
Cookie 管理CookieManagerCookieHandlerCookieJar (更靈活)
多部分上傳手工拼裝手工/BodyPublisherMultipartBody.Builder 簡潔
WebSocket無內建內建
依賴大小0 (JDK 內建)0 (JDK 內建)~400KB (okhttp + okio)
最低 JDK1.0118+ (Android 5+)

5. 遷移建議

  1. JDK 8 專案:繼續用 HttpURLConnection 封裝或引入 OkHttp (推薦),獲得現代 API 與效能。
  2. JDK 11+ 新專案:優先 java.net.http.HttpClient,零依賴、標準化、效能優異;需攔截器/重試時整合 Resilience4j。
  3. 高併發/微服務/Android:OkHttp 是事實標準,攔截器生態豐富 (Logging、Auth、Gzip、Cache)、連線池調優細緻、支援 HTTP/3 (QUIC) 實驗。
  4. Spring Boot 專案:WebClient (WebFlux) 或 RestClient (Spring 6.1+) 封裝底層 Client,提供宣告式 API、自動重試、觀測性整合。

6. 最佳實踐清單 (通用)

  • 全域單一 Client 實例,複用連線池。
  • 所有請求設定連線/讀取超時 (建議 5s/15s 起)。
  • 啟用 GZIP/Deflate 壓縮 (Accept-Encoding: gzip, deflate)。
  • 敏感 Header (Authorization) 日誌遮罩。
  • 冪等 API (GET/HEAD/PUT/DELETE) 才自動重試;POST 預設不重試。
  • 錯誤回應體 (errorStream) 一定要讀取並記錄,否則連線無法釋放。
  • 憑證驗證:生產環境務必啟用 HTTPS 驗證,測試環境才用信任所有策略。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved