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

Java 原生 HttpURLConnection 發送 HTTP 請求完整指南:GET/POST、超時、代理、流式處理

     編輯:Java 相關知識

前言

雖然 HttpClient、OkHttp、WebClient 更便利,但 java.net.HttpURLConnection (底層 URLConnection) 是零依賴、JDK 內建的選擇,適合輕量級場景、受限環境或理解 HTTP 協議細節。本文提供生產級封裝類 HttpRequestor,支援 GET/POST、連線/讀取超時、HTTP/HTTPS 代理、字元編碼、資源安全釋放。

核心封裝類:HttpRequestor

 params) throws IOException {
        String body = buildFormBody(params);
        HttpURLConnection conn = createConnection(new URL(url), "POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=" + charset);
        conn.setRequestProperty("Content-Length", Integer.toString(body.getBytes(charset).length));
        return executeRequest(conn, body);
    }

    // ----- 通用 JSON POST -----
    public String doPostJson(String url, String json) throws IOException {
        HttpURLConnection conn = createConnection(new URL(url), "POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/json; charset=" + charset);
        return executeRequest(conn, json);
    }

    // ----- 核心執行邏輯 -----
    private String executeRequest(HttpURLConnection conn, String requestBody) throws IOException {
        try {
            if (requestBody != null) {
                try (OutputStream os = conn.getOutputStream();
                     OutputStreamWriter osw = new OutputStreamWriter(os, charset)) {
                    osw.write(requestBody);
                    osw.flush();
                }
            }

            int status = conn.getResponseCode();
            if (status >= 300) {
                // 讀取錯誤流以獲取伺服器錯誤詳情
                String error = readStream(conn.getErrorStream());
                throw new IOException("HTTP " + status + ": " + error);
            }
            return readStream(conn.getInputStream());
        } finally {
            conn.disconnect(); // 釋放連線資源
        }
    }

    private String readStream(InputStream is) throws IOException {
        if (is == null) return "";
        try (BufferedReader br = new BufferedReader(new InputStreamReader(is, charset))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) sb.append(line);
            return sb.toString();
        }
    }

    private HttpURLConnection createConnection(URL url, String method) throws IOException {
        URLConnection uc;
        if (proxyHost != null && proxyPort != null) {
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
            uc = url.openConnection(proxy);
        } else {
            uc = url.openConnection();
        }
        HttpURLConnection conn = (HttpURLConnection) uc;
        conn.setRequestMethod(method);
        conn.setConnectTimeout(connectTimeout);
        conn.setReadTimeout(readTimeout);
        conn.setRequestProperty("Accept-Charset", charset);
        conn.setRequestProperty("User-Agent", "Java HttpRequestor/1.0");
        return conn;
    }

    private String buildFormBody(Map params) {
        if (params == null || params.isEmpty()) return "";
        StringBuilder sb = new StringBuilder();
        for (Map.Entry e : params.entrySet()) {
            if (sb.length() > 0) sb.append('&');
            sb.append(encode(e.getKey())).append('=').append(encode(e.getValue()));
        }
        return sb.toString();
    }

    private String encode(String s) {
        try { return URLEncoder.encode(s, charset); }
        catch (UnsupportedEncodingException e) { throw new RuntimeException(e); }
    }

    // ----- Setters -----
    public void setCharset(String charset) { this.charset = charset; }
    public void setConnectTimeout(Integer ms) { this.connectTimeout = ms; }
    public void setReadTimeout(Integer ms) { this.readTimeout = ms; }
    public void setProxy(String host, int port) { this.proxyHost = host; this.proxyPort = port; }
}

使用範例

 form = new HashMap<>();
form.put("username", "test");
form.put("password", "123456");
String result = new HttpRequestor().doPost("https://httpbin.org/post", form);

// POST JSON
String json = "{\"name\":\"test\",\"age\":25}";
String jsonResult = new HttpRequestor().doPostJson("https://httpbin.org/post", json);

// 設定代理與超時
HttpRequestor client = new HttpRequestor();
client.setProxy("proxy.company.com", 3128);
client.setConnectTimeout(3000);
client.setReadTimeout(10000);

Servlet 端測試回應 (對應原文 LoginServlet)


關鍵最佳實務

實務說明
必須設定超時預設無限期阻塞,導致執行緒耗盡。connectTimeout (建立 TCP)、readTimeout (讀取資料)。
try-with-resources 管理流確保 InputStream/OutputStream 關閉,釋放連線回連線池 (keep-alive)。
讀取錯誤流非 2xx 回應時,getErrorStream() 含伺服器錯誤訊息,getInputStream() 會拋異常。
指定 Charset請求/回應皆明確 UTF-8,避免平台預設編碼差異。
大檔案/流式處理勿一次 readAllBytes()。使用 InputStream 逐塊處理或 Files.copy(is, path)。
HTTPS 憑證信任生產環境需正規 CA 憑證。測試環境可安裝自簽憑證到 cacerts 或實作 TrustManager (不建議生產用)。
連線重用HttpURLConnection 內建 Keep-Alive 連線池 (由 http.keepAlive, http.maxConnections 系統屬性控制)。

異步回調模式 (原文 HttpListenerServlet 對應)

若需異步接收第三方回調 (如支付通知),Servlet 只需讀取 request.getInputStream() 即可獲取原始 Body,無需參數解析:

 process(payload));
        resp.setStatus(HttpServletResponse.SC_OK); // 立即回應 200
    }
}

總結

HttpURLConnection 雖 API 較繁瑣,但掌握其連線建立 → 設定屬性 → 輸出請求體 → 讀取回應碼/流 → 釋放資源 標準流程,能在無第三方庫環境下快速實現 HTTP 客戶端功能。生產系統長期維護建議遷移至 HttpClient 5 / OkHttp / WebClient,獲得連線池管理、重試、熔斷、HTTP/2、非阻塞 I/O 等企業級特性。

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