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

Java 網路程式設計:HTTP 請求完整實戰指南

     編輯:Java 相關知識

Java HTTP 請求核心概念與實作

HTTP 是 Web 通訊的基石。本文詳細解析 HTTP 請求結構、常見標頭,並提供 GET/POST 完整實作範例,涵蓋圖片下載、表單提交、參數編碼等實戰場景。

HTTP 請求結構

GET /api/users?id=123&name=tom HTTP/1.1
Host: example.com
Accept: application/json
User-Agent: Mozilla/5.0
Connection: keep-alive

[請求體 - GET 通常為空]

請求行三要素

  • 方法:GET、POST、PUT、DELETE、HEAD、OPTIONS、TRACE、PATCH
  • URL:資源路徑 + 查詢字串 (Query String)
  • 版本:HTTP/1.1、HTTP/2、HTTP/3

GET vs POST 核心差異

特性GETPOST
參數位置URL 查詢字串 (?key=val&...)請求體
資料長度受限 (~2KB~8KB,視瀏覽器/伺服器)無限制
安全性參數暴露在 URL,不適合敏感資料參數在請求體,相對安全
快取可被快取、書籤、歷史記錄預設不快取
冪等性冪等非冪等
編碼類型application/x-www-form-urlencoded支援 multipart/form-data (檔案上傳)

常見請求標頭

標頭用途範例
Accept客戶端可處理的媒體類型application/json, text/html
Accept-Charset支援的字元集UTF-8, ISO-8859-1
Accept-Encoding支援的壓縮格式gzip, deflate, br
Accept-Language偏好語言zh-TW, zh;q=0.9, en;q=0.8
Host目標主機 (必須)api.example.com:8080
User-Agent客戶端識別資訊Mozilla/5.0 / Java/17
Content-Type請求體媒體類型application/json; charset=UTF-8
Content-Length請求體長度 (位元組)1024
Authorization認證資訊Bearer eyJhbGciOiJIUzI1NiIs...
Cookie攜帶 CookiesessionId=abc123; token=xyz
Referer來源頁面 (防盜鏈)https://example.com/page
Connection連線管理keep-alive, close

實戰一:GET 請求下載圖片

public class HttpGetDemo {
    private static final String IMAGE_URL = "https://httpbin.org/image/png";
    private static final String SAVE_PATH = "downloaded_image.png";
    
    public static void main(String[] args) {
        downloadImage();
    }
    
    public static void downloadImage() {
        try (InputStream in = createInputStream();
             FileOutputStream out = new FileOutputStream(SAVE_PATH)) {
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
            System.out.println("圖片下載完成: " + SAVE_PATH);
            
        } catch (IOException e) {
            System.err.println("下載失敗: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    private static InputStream createInputStream() throws IOException {
        URL url = new URL(IMAGE_URL);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        
        // 關鍵配置
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);      // 連線超時 5 秒
        conn.setReadTimeout(10000);        // 讀取超時 10 秒
        conn.setRequestProperty("User-Agent", "Java-HttpClient/1.0");
        conn.setRequestProperty("Accept", "image/*,*/*;q=0.8");
        
        int responseCode = conn.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK) {
            throw new IOException("HTTP 錯誤碼: " + responseCode);
        }
        
        return conn.getInputStream();
    }
}

實戰二:POST 請求提交表單資料

public class HttpPostDemo {
    private static final String API_URL = "https://httpbin.org/post";
    
    public static void main(String[] args) {
        Map formData = new HashMap<>();
        formData.put("username", "admin");
        formData.put("password", "secret123");
        formData.put("remember", "true");
        
        String response = sendPostForm(formData, "UTF-8");
        System.out.println("伺服器回應: " + response);
    }
    
    public static String sendPostForm(Map params, String charset) {
        if (params == null || params.isEmpty()) return "";
        
        // 1. 建構表單資料
        StringBuilder formData = new StringBuilder();
        try {
            for (Map.Entry entry : params.entrySet()) {
                if (formData.length() > 0) formData.append('&');
                formData.append(URLEncoder.encode(entry.getKey(), charset))
                        .append('=')
                        .append(URLEncoder.encode(entry.getValue(), charset));
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("編碼不支援: " + charset, e);
        }
        
        byte[] postData = formData.toString().getBytes(StandardCharsets.UTF_8);
        
        // 2. 發送請求
        try {
            URL url = new URL(API_URL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            
            conn.setRequestMethod("POST");
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(10000);
            conn.setDoOutput(true);                    // 允許輸出
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=" + charset);
            conn.setRequestProperty("Content-Length", String.valueOf(postData.length));
            conn.setRequestProperty("User-Agent", "Java-HttpClient/1.0");
            
            // 寫入請求體
            try (OutputStream out = conn.getOutputStream()) {
                out.write(postData);
                out.flush();
            }
            
            // 讀取回應
            int responseCode = conn.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                return readResponse(conn.getInputStream(), charset);
            } else {
                String error = readResponse(conn.getErrorStream(), charset);
                throw new IOException("HTTP " + responseCode + ": " + error);
            }
            
        } catch (IOException e) {
            throw new RuntimeException("POST 請求失敗", e);
        }
    }
    
    private static String readResponse(InputStream in, String charset) throws IOException {
        if (in == null) return "";
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        }
    }
}

實戰三:POST 發送 JSON 資料 (現代 API 常用)

public static String sendPostJson(String jsonBody, String url) {
    try {
        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setRequestMethod("POST");
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(10000);
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        conn.setRequestProperty("Accept", "application/json");
        
        try (OutputStream out = conn.getOutputStream()) {
            out.write(jsonBody.getBytes(StandardCharsets.UTF_8));
        }
        
        int code = conn.getResponseCode();
        InputStream stream = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
        return readResponse(stream, "UTF-8");
        
    } catch (IOException e) {
        throw new RuntimeException("JSON POST 失敗", e);
    }
}

// 使用範例
String json = "{\"name\":\"張三\",\"age\":25,\"skills\":[\"Java\",\"Spring\"]}";
String result = sendPostJson(json, "https://api.example.com/users");

最佳實踐與常見坑點

⚠️ 必須注意

  • 永遠設定超時:setConnectTimeout() + setReadTimeout(),避免執行緒永久阻塞
  • 關閉資源:使用 try-with-resources 確保 InputStream/OutputStream 正確關閉
  • 處理錯誤流:非 2xx 回應需讀取 getErrorStream() 取得錯誤詳情
  • URL 編碼:參數值必須用 URLEncoder.encode(),避免特殊字元導致錯誤
  • 重導向處理:預設自動跟隨,可用 setInstanceFollowRedirects(false) 關閉

✅ 建議做法

  • 生產環境建議使用 Apache HttpClient 或 OkHttp,功能更豐富、效能更好
  • Java 11+ 可使用內建 java.net.http.HttpClient (支援 HTTP/2、非同步、CompletableFuture)
  • 封裝通用工具類,統一錯誤處理、日誌、重試邏輯
  • 敏感資訊 (密碼、Token) 勿記錄在日誌中

掌握原生 HttpURLConnection 雖然較繁瑣,但能深入理解 HTTP 協議細節。實務開發建議根據專案需求選擇適合的 HTTP 客戶端庫,並建立統一的請求封裝層。

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