HTTP Methods(上):GET 與 POST 的本質差異
GET 與 POST 是 HTTP 中最常用的兩個方法,但它們的差異遠不止「GET 用來取資料、POST 用來送資料」這麼簡單。本文將從協定規範的角度,深入探討它們的本質差異。
一、 HTTP Methods 概述
HTTP 方法(Method)定義了客戶端希望對資源執行的操作類型。RFC 9110 定義了以下標準方法:
| 方法 | 用途 | 安全 | 冪等 |
|---|---|---|---|
| GET | 獲取資源 | ✅ | ✅ |
| HEAD | 獲取標頭(不含 Body) | ✅ | ✅ |
| POST | 提交資料 | ❌ | ❌ |
| PUT | 替換資源 | ❌ | ✅ |
| PATCH | 部分更新 | ❌ | ❌ |
| DELETE | 刪除資源 | ❌ | ✅ |
| OPTIONS | 查詢支援的方法 | ✅ | ✅ |
| TRACE | 迴路測試 | ✅ | ✅ |
| CONNECT | 建立隧道 | ❌ | ❌ |
> **安全(Safe)**:該操作不會改變伺服器上的資源狀態。
冪等(Idempotent):多次執行相同操作,結果都一樣。
二、 GET 方法深入解析
2.1 規範定義
根據 RFC 9110:
GET 方法請求轉移目標資源的當前選定的表示。
簡單來說:GET 就是「給我看看這個資源目前的樣子」。
2.2 GET 的特性
安全性(Safe)
GET 請求不應該產生副作用。這意味著:
✅ 正確用法:
GET /api/users/123 → 查詢用戶資料
GET /api/articles?page=2 → 獲取文章列表
❌ 錯誤用法:
GET /api/users/123/delete → 不應該用 GET 來刪除
GET /api/cart/checkout → 結帳會產生副作用WARNING
雖然技術上你可以用 GET 來修改資料,但這違反了 HTTP 語義,可能導致:
- 瀏覽器/代理隨意重送請求
- 搜尋引擎爬蟲誤觸發操作
- 預取(Prefetch)功能造成意外行為
冪等性(Idempotent)
無論發送多少次相同的 GET 請求,伺服器狀態都不會改變:
GET /api/users/123 → { id: 123, name: "John" }
GET /api/users/123 → { id: 123, name: "John" } // 結果相同
GET /api/users/123 → { id: 123, name: "John" } // 伺服器沒變可快取(Cacheable)
GET 請求的回應預設是可快取的:
GET /api/articles/42 HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Cache-Control: max-age=3600
ETag: "abc123"
{...}瀏覽器、CDN、代理伺服器都可以快取 GET 回應。
2.3 GET 請求的結構
GET /api/users?role=admin&page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGc...特點:
- 參數放在 URL 的 Query String 中
- 沒有 Request Body(規範允許但不建議)
- URL 長度有限制(約 2048 字元,視瀏覽器而定)
2.4 GET 的限制
| 項目 | 說明 |
|---|---|
| URL 長度 | 約 2000-8000 字元(視平台而定) |
| 資料類型 | 僅能傳送文字(需 URL 編碼) |
| 隱私性 | 參數暴露在 URL 中,可能被記錄 |
| 快取風險 | 敏感資訊可能被快取 |
三、 POST 方法深入解析
3.1 規範定義
根據 RFC 9110:
POST 方法請求目標資源根據資源自身的語義,處理請求中包含的表示。
簡單來說:POST 是「給你這些資料,請你處理」。
3.2 POST 的特性
非安全性(Unsafe)
POST 可以產生副作用,這是被允許且預期的:
✅ 正確用法:
POST /api/users → 創建新用戶
POST /api/orders → 創建訂單
POST /api/files/upload → 上傳檔案
POST /api/comments → 發表評論非冪等性(Non-Idempotent)
多次執行相同的 POST 請求,可能產生不同結果:
POST /api/orders → 創建訂單 #1001
POST /api/orders → 創建訂單 #1002 // 又創建了一個!
POST /api/orders → 創建訂單 #1003 // 再創建一個!CAUTION
這就是為什麼瀏覽器在重新整理 POST 頁面時會警告「確定要重新提交表單嗎?」
預設不快取
POST 回應預設不會被快取,除非明確設置:
HTTP/1.1 200 OK
Cache-Control: no-store // 通常會這樣設置3.3 POST 請求的結構
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 58
{
"name": "John Doe",
"email": "john@example.com"
}特點:
- 資料放在 Request Body 中
- 必須指定
Content-Type - 理論上無大小限制(受伺服器配置影響)
3.4 常見的 Content-Type
| Content-Type | 用途 | 範例 |
|---|---|---|
application/json | JSON 格式(最常用) | {"name": "John"} |
application/x-www-form-urlencoded | 表單預設格式 | name=John&email=... |
multipart/form-data | 檔案上傳 | 含有邊界分隔的多部分 |
text/plain | 純文字 | Hello World |
四、 GET vs POST 核心差異
4.1 比較總覽
| 面向 | GET | POST |
|---|---|---|
| 語意 | 獲取資源 | 處理資料 |
| 資料位置 | URL Query String | Request Body |
| 安全性 | 安全(不改變狀態) | 不安全(可能改變狀態) |
| 冪等性 | 冪等 | 不冪等 |
| 快取 | 預設可快取 | 預設不快取 |
| 書籤 | 可加入書籤 | 無法加入書籤 |
| 歷史記錄 | 會保留在瀏覽器歷史 | 不會保留完整資訊 |
| 資料長度 | 受 URL 長度限制 | 無限制(受伺服器設定) |
| 資料型別 | 僅文字 | 任何型別 |
| 瀏覽器後退 | 無副作用 | 會提示重新提交 |
4.2 視覺化比較
4.3 安全性常見誤解
> **「POST 比 GET 安全」是錯誤的觀念!**
兩者在傳輸層面的安全性完全相同:
| 誤解 | 事實 |
|---|---|
| POST 資料被加密 | ❌ HTTP 本身不加密,要靠 HTTPS |
| GET 參數會被看到 | ⚠️ HTTPS 下 URL 也被加密(但可能被記錄) |
| POST 更難被攔截 | ❌ 用抓包工具一樣能看到 Body |
真正的差異在於:
- GET 參數會出現在瀏覽器歷史、書籤、伺服器日誌
- POST Body 通常不會被這些地方記錄
五、 實戰情境分析
5.1 情境:搜尋功能
應該用 GET:
GET /api/products?keyword=laptop&category=electronics&sort=price原因:
- 搜尋不會改變伺服器狀態 → 安全
- 結果可以被快取
- URL 可分享、可加入書籤
- 瀏覽器後退不會有問題
5.2 情境:登入
應該用 POST:
POST /api/auth/login HTTP/1.1
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret123"
}原因:
- 密碼不應出現在 URL 中
- 登入狀態會改變(建立 Session)
- 不應被快取
- 不應被重放
5.3 情境:分頁 API
應該用 GET:
GET /api/articles?page=2&limit=10原因:
- 只是查詢,不改變狀態
- 可分享特定頁面連結
- CDN 可快取各頁面
5.4 情境:表單提交
應該用 POST:
POST /api/contact HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=John&email=john@example.com&message=Hello原因:
- 會創建新的聯絡紀錄
- 資料量可能較大
- 不應被重複提交
六、 POST-Redirect-GET 模式
為了避免使用者重新整理後重複提交表單,常用 PRG 模式:
實作程式碼:
// Express 後端
app.post("/api/orders", async (req, res) => {
const order = await createOrder(req.body);
res.redirect(303, `/orders/${order.id}`);
});總結
| 問題 | 答案 |
|---|---|
| 何時用 GET? | 獲取資料、搜尋、分頁、可分享的連結 |
| 何時用 POST? | 創建資源、登入、表單提交、檔案上傳 |
| GET 限制? | URL 長度、僅純文字、參數暴露 |
| POST 風險? | 非冪等,可能重複提交 |
> **簡單判斷法**:問自己「這個操作會改變伺服器上的資料嗎?」
- 不會 → GET
- 會 → POST(或其他適當的方法)
進階挑戰
- 使用瀏覽器開發者工具,觀察一個 GET 請求和一個 POST 請求的差異(Headers、Payload、Timing)。
- 嘗試設計一個 API:用戶可以「按讚」一篇文章,你會用 GET 還是 POST?為什麼?如果要求冪等(按第二次取消讚),又該怎麼設計?
- 實作一個 PRG 模式的表單提交流程。