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

Java HTTP 客戶端實戰:GET、POST、代理訪問與工具類封裝

     編輯:Java 相關知識

前言

雖然現代項目多使用 HttpClient、OkHttp、WebClient 等成熟庫,但理解 JDK 原生 HttpURLConnection 的底層交互細節,對排查網絡問題、適配特殊環境(如無法引入第三方依賴的舊項目)仍有重要價值。本文提供一個生產可用的工具類,涵蓋 GET、POST、HTTPS、代理、超時控制、資源釋放等核心場景。

核心工具類:HttpRequestUtil

public class HttpRequestUtil {

    // ===== 代理配置(可外部化注入)=====
    private static boolean proxyEnabled = false;
    private static String proxyHost = "127.0.0.1";
    private static int proxyPort = 8087;

    public static void setProxy(String host, int port) {
        proxyEnabled = true;
        proxyHost = host;
        proxyPort = port;
    }

    // ===== 通用異步執行入口 =====
    private static HttpURLConnection openConnection(String urlStr, String method, boolean useProxy) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn;
        if (useProxy && proxyEnabled) {
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
            conn = (HttpURLConnection) url.openConnection(proxy);
        } else {
            conn = (HttpURLConnection) url.openConnection();
        }
        conn.setRequestMethod(method);
        conn.setConnectTimeout(10_000); // 連接超時 10s
        conn.setReadTimeout(30_000);    // 讀取超時 30s
        conn.setUseCaches(false);
        // 通用請求頭
        conn.setRequestProperty("Accept", "*/*");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Java HttpClient)");
        return conn;
    }

    // ===== 讀取響應流通用邏輯 =====
    private static String readResponse(HttpURLConnection conn) throws IOException {
        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();
        } catch (IOException e) {
            // 嘗試讀取錯誤流
            try (InputStream es = conn.getErrorStream();
                 BufferedReader br = new BufferedReader(new InputStreamReader(es, StandardCharsets.UTF_8))) {
                StringBuilder sb = new StringBuilder();
                String line;
                while ((line = br.readLine()) != null) sb.append(line);
                throw new IOException("HTTP " + conn.getResponseCode() + ": " + sb.toString(), e);
            }
        }
    }

    // ===== 公開 API =====

    /** 發送 GET 請求,參數自動拼接到 URL */
    public static String sendGet(String baseUrl, Map<String, String> params, boolean useProxy) {
        String url = buildUrlWithParams(baseUrl, params);
        try {
            HttpURLConnection conn = openConnection(url, "GET", useProxy);
            conn.connect();
            return readResponse(conn);
        } catch (IOException e) {
            throw new RuntimeException("GET request failed: " + url, e);
        }
    }

    /** 發送 POST 請求,表單數據寫入請求體 */
    public static String sendPost(String url, String formData, boolean useProxy) {
        try {
            HttpURLConnection conn = openConnection(url, "POST", useProxy);
            conn.setDoOutput(true); // 關鍵:允許輸出流
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            conn.connect();
            try (OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)) {
                osw.write(formData);
                osw.flush();
            }
            return readResponse(conn);
        } catch (IOException e) {
            throw new RuntimeException("POST request failed: " + url, e);
        }
    }

    /** 發送 POST JSON 請求 */
    public static String sendPostJson(String url, String jsonBody, boolean useProxy) {
        try {
            HttpURLConnection conn = openConnection(url, "POST", useProxy);
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            conn.connect();
            try (OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)) {
                osw.write(jsonBody);
                osw.flush();
            }
            return readResponse(conn);
        } catch (IOException e) {
            throw new RuntimeException("POST JSON request failed: " + url, e);
        }
    }

    /** 下載文件/圖片返回輸入流(需調用者關閉) */
    public static InputStream downloadStream(String url, boolean useProxy) throws IOException {
        HttpURLConnection conn = openConnection(url, "GET", useProxy);
        conn.connect();
        if (conn.getResponseCode() != 200) {
            throw new IOException("Download failed: HTTP " + conn.getResponseCode());
        }
        return conn.getInputStream(); // 注意:調用者必須在 finally 中關閉流並調用 conn.disconnect()
    }

    // ===== 輔助方法 =====
    private static String buildUrlWithParams(String baseUrl, Map<String, String> params) {
        if (params == null || params.isEmpty()) return baseUrl;
        StringBuilder sb = new StringBuilder(baseUrl);
        sb.append(baseUrl.contains("?") ? "&" : "?");
        params.forEach((k, v) -> {
            try {
                sb.append(URLEncoder.encode(k, "UTF-8")).append('=')
                  .append(URLEncoder.encode(v, "UTF-8")).append('&');
            } catch (UnsupportedEncodingException ignored) {}
        });
        sb.deleteCharAt(sb.length() - 1); // 移除最後的 &
        return sb.toString();
    }
}

使用示例

1. 簡單 GET 請求

String html = HttpRequestUtil.sendGet("https://api.example.com/users", 
    Map.of("page", "1", "size", "20"), false);
System.out.println(html);

2. 表單 POST 請求

String form = "username=admin&password=123456";
String result = HttpRequestUtil.sendPost("https://api.example.com/login", form, false);
System.out.println(result);

3. JSON POST 請求(現代 API 常用)

String json = "{\"name\":\"張三\",\"age\":25}";
String resp = HttpRequestUtil.sendPostJson("https://api.example.com/users", json, false);

4. 代理模式(如 Fiddler/Charles 抓包調試)

HttpRequestUtil.setProxy("127.0.0.1", 8888); // 設置代理
String viaProxy = HttpRequestUtil.sendGet("https://httpbin.org/ip", null, true);
System.out.println("代理出口 IP: " + viaProxy);

5. 文件下載

try (InputStream is = HttpRequestUtil.downloadStream("https://example.com/logo.png", false);
     FileOutputStream fos = new FileOutputStream("logo.png")) {
    byte[] buffer = new byte[8192];
    int len;
    while ((len = is.read(buffer)) != -1) fos.write(buffer, 0, len);
} catch (IOException e) {
    log.error("下載失敗", e);
}

關鍵最佳實踐總結

  1. 超時必須雙設置:setConnectTimeout(建立連接)+ setReadTimeout(讀取數據),防止線程無限掛起。
  2. 資源釋放用 try-with-resources:JDK 7+ 自動關閉流、Writer,即使異常也能釋放連接,避免連接洩漏導致連接池耗盡。
  3. 錯誤流讀取:非 2xx 響應需讀取 getErrorStream() 獲取服務端錯誤詳情,否則連接無法復用。
  4. 編碼顯式指定 UTF-8:StandardCharsets.UTF_8 避免平台默認編碼不一致導致亂碼。
  5. 代理類型選擇:Proxy.Type.HTTP 適用 HTTP/HTTPS 隧道;SOCKS5 代理用 Proxy.Type.SOCKS。
  6. HTTPS 憑證信任:生產環境請勿使用信任所有憑證的 TrustManager,應導入正規 CA 憑證或配置信任庫。

遷移建議

新項目強烈推薦使用 Apache HttpClient 5.x(連接池、重試、攔截器、異步支持完善)或 OkHttp(HTTP/2、連接復用、現代 API 設計)。僅在極度受限環境(如 JDK 8 無依賴、嵌入式設備)下使用原生 HttpURLConnection。

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