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

Java 模擬 HTTP GET/POST 請求實戰:從原生 HttpURLConnection 到高級封裝最佳實踐

     編輯:Java 相關知識

前言

在 Java 中發送 HTTP 請求是系統整合、爬蟲、第三方 API 調用的基礎技能。從 JDK 內建的 `HttpURLConnection`、Apache HttpClient、OkHttp 到 JDK 11+ 的 `HttpClient`,選型與封裝直接影響代碼可維護性與穩定性。本文以**原生 HttpURLConnection 手寫 GET/POST** 為基礎,進階至**通用請求封裝類**,並對比主流客戶端庫選型建議。

一、HttpURLConnection 基礎用法

GET 請求

public static String doGet(String urlString, Map headers) throws IOException {
    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5000);
    conn.setReadTimeout(10000);
    // 設置請求頭
    if (headers != null) headers.forEach(conn::setRequestProperty);
    // 處理響應
    try (InputStream is = conn.getInputStream();
         BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) sb.append(line);
        return sb.toString();
    } finally {
        conn.disconnect();
    }
}

POST 請求 (表單/JSON)


public static String doPost(String urlString, String body, String contentType, Map headers) throws IOException {
    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("POST");
    conn.setDoOutput(true); // 允許輸出流
    conn.setConnectTimeout(5000);
    conn.setReadTimeout(10000);
    conn.setRequestProperty("Content-Type", contentType); // application/json / application/x-www-form-urlencoded
    if (headers != null) headers.forEach(conn::setRequestProperty);

    try (OutputStream os = conn.getOutputStream()) {
        os.write(body.getBytes(StandardCharsets.UTF_8));
        os.flush();
    }

    int code = conn.getResponseCode();
    InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
    try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) sb.append(line);
        if (code >= 400) throw new HttpException(code, sb.toString());
        return sb.toString();
    } finally {
        conn.disconnect();
    }
}

二、通用請求封裝類 HttpClientUtil (建議收藏)


public class HttpClientUtil {
    private static final int CONNECT_TIMEOUT = 5000;
    private static final int READ_TIMEOUT = 15000;

    // 鏈式調用構建器
    public static class RequestBuilder {
        private String url;
        private String method = "GET";
        private Map headers = new HashMap<>();
        private Map params = new LinkedHashMap<>(); // GET 參數 / POST 表單
        private String jsonBody; // JSON 字串
        private String contentType = "application/x-www-form-urlencoded";

        public RequestBuilder url(String url) { this.url = url; return this; }
        public RequestBuilder method(String method) { this.method = method; return this; }
        public RequestBuilder header(String k, String v) { headers.put(k, v); return this; }
        public RequestBuilder param(String k, Object v) { params.put(k, v); return this; }
        public RequestBuilder jsonBody(String json) { this.jsonBody = json; this.contentType = "application/json"; return this; }
        public RequestBuilder formParams(Map params) { this.params.putAll(params); return this; }

        public String execute() throws IOException {
            // 1. 構建最終 URL (GET 參數拼接)
            String finalUrl = url;
            if (!params.isEmpty() && ("GET".equalsIgnoreCase(method) || jsonBody == null)) {
                String query = params.entrySet().stream()
                    .map(e -> e.getKey() + "=" + URLEncoder.encode(String.valueOf(e.getValue()), StandardCharsets.UTF_8))
                    .collect(Collectors.joining("&"));
                finalUrl += (url.contains("?") ? "&" : "?") + query;
            }

            // 2. 開啟連線
            HttpURLConnection conn = (HttpURLConnection) new URL(finalUrl).openConnection();
            conn.setRequestMethod(method);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setReadTimeout(READ_TIMEOUT);
            headers.forEach(conn::setRequestProperty);

            // 3. 處理請求體 (POST/PUT/PATCH)
            if (("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method) || "PATCH".equalsIgnoreCase(method)) && jsonBody != null) {
                conn.setDoOutput(true);
                conn.setRequestProperty("Content-Type", contentType);
                try (OutputStream os = conn.getOutputStream()) {
                    os.write(jsonBody.getBytes(StandardCharsets.UTF_8));
                }
            } else if (("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && !params.isEmpty() && jsonBody == null) {
                // 表單提交
                conn.setDoOutput(true);
                conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                String formBody = params.entrySet().stream()
                    .map(e -> e.getKey() + "=" + URLEncoder.encode(String.valueOf(e.getValue()), StandardCharsets.UTF_8))
                    .collect(Collectors.joining("&"));
                try (OutputStream os = conn.getOutputStream()) {
                    os.write(formBody.getBytes(StandardCharsets.UTF_8));
                }
            }

            // 4. 讀取響應
            int code = conn.getResponseCode();
            InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
            try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
                String body = br.lines().collect(Collectors.joining("\n"));
                if (code >= 400) throw new HttpException(code, body, headers);
                return body;
            } finally {
                conn.disconnect();
            }
        }
    }

    // 靜態入口
    public static RequestBuilder create() { return new RequestBuilder(); }

    // 異常類
    public static class HttpException extends IOException {
        private final int statusCode;
        private final Map responseHeaders;
        public HttpException(int code, String message) { this(code, message, null); }
        public HttpException(int code, String message, Map headers) {
            super("HTTP " + code + ": " + message);
            this.statusCode = code;
            this.responseHeaders = headers;
        }
        public int getStatusCode() { return statusCode; }
        public Map getResponseHeaders() { return responseHeaders; }
    }
}

三、使用範例


// GET 請求帶參數與 Header
String getResp = HttpClientUtil.create()
    .url("https://api.example.com/users")
    .method("GET")
    .param("page", 1)
    .param("size", 20)
    .header("Authorization", "Bearer token123")
    .execute();

// POST JSON
String json = "{\"name\":\"Alice\",\"age\":25}";
String postResp = HttpClientUtil.create()
    .url("https://api.example.com/users")
    .method("POST")
    .jsonBody(json)
    .header("Authorization", "Bearer token123")
    .execute();

// POST 表單
String formResp = HttpClientUtil.create()
    .url("https://api.example.com/login")
    .method("POST")
    .param("username", "admin")
    .param("password", "123456")
    .execute();

四、主流 HTTP 客戶端庫對比與選型建議

庫版本要求核心優勢適用場景
HttpURLConnectionJDK 1.1+零依賴、標準 API、輕量簡單調用、受限環境無法引入第三方 Jar、Android 早期版本
Apache HttpClient 4.5/5.xJDK 1.7+連線池、重試、攔截器、多部分上傳、成熟穩定企業級應用、複雜 HTTP 交互、需連線池管理、Spring RestTemplate 底層
OkHttp 3.x/4.xJDK 1.8+HTTP/2、連線池、透明 GZIP、攔截器鏈、同步/異步、Kotlin 友好高性能要求、Android 主流、Retrofit 底層、微服務調用
JDK 11+ HttpClientJDK 11+標準化、HTTP/2、WebSocket、CompletableFuture 異步、零依賴JDK 11+ 專案、不想引入第三方、現代化異步編程
Spring WebClient / RestTemplateSpring 5+聲明式、響應式 (WebClient)、整合 Spring 生態、負載均衡、熔斷Spring Boot / Spring Cloud 微服務調用

五、生產環境必備增強清單

  1. ✅ **連線池**:複用 TCP 連線,減少握手開銷 (HttpClient/OkHttp/JDK11 HttpClient 內建)。
  2. ✅ **超時控制**:連線超時、讀取超時、寫入超時、**全局請求超時**,防止線程掛死。
  3. ✅ **重試機制**:冪等請求 (GET/HEAD/PUT/DELETE) 自動重試 (指數退避 + 抖動),可配置重試條件 (5xx、網絡異常)。
  4. ✅ **熔斷降級**:Resilience4j / Sentinel 整合,防止雪崩。
  5. ✅ **請求/響應日誌**:記錄 URL、方法、耗時、狀態碼、請求 ID (TraceID),敏感字段脫敏。
  6. ✅ **SSL/TLS 配置**:信任憑證、雙向認證、主機名驗證。
  7. ✅ **異步非阻塞**:高併發場景使用 `CompletableFuture` / `WebFlux` / `OkHttp Call.enqueue()` 釋放執行緒。
  8. ✅ **指標監控**:Micrometer + Prometheus 導出 `http.client.requests`、`http.client.latency`、`http.client.errors`。

六、結語

理解 `HttpURLConnection` 底層原理是掌握高級客戶端的前提。實務中:**新專案 JDK 11+ 首選標準 `HttpClient` (零依賴、現代 API);Spring Boot 專案直接用 `WebClient`/`RestTemplate`;高性能/微服務/Andorid 選 `OkHttp`;老舊企業級應用維護用 `HttpClient`**。封裝統一 `HttpClientUtil` 介面,隔離底層實現,才能靈活遷移與演進。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved