跳至主要內容
Skip to content

HTTP Methods(下):PUT、PATCH、DELETE、OPTIONS、HEAD 完全解析

在上一篇中,我們深入探討了 GET 與 POST 的本質差異。本篇將繼續介紹其他重要的 HTTP 方法,它們在 RESTful API 設計中扮演著不可或缺的角色。


一、 PUT 方法:完整替換資源

1.1 規範定義

根據 RFC 9110:

PUT 方法請求創建替換目標資源的狀態為請求內容所定義的狀態。

簡單來說:PUT 是「用我給的內容,完整取代這個資源」。

1.2 PUT 的特性

特性說明
安全性❌ 不安全(會修改資源)
冪等性✅ 冪等(多次執行結果相同)
快取❌ 不可快取

為什麼 PUT 是冪等的?

http
PUT /api/users/123 HTTP/1.1
Content-Type: application/json

{
  "name": "John",
  "email": "john@example.com",
  "age": 30
}

無論執行幾次:

  • 第一次:資源被更新為 {name: "John", email: "john@example.com", age: 30}
  • 第二次:資源仍然是 {name: "John", email: "john@example.com", age: 30}
  • 第 N 次:結果完全相同

1.3 PUT vs POST

面向POSTPUT
URI 決定者伺服器客戶端
多次執行可能創建多個資源結果相同
適用情境新增(ID 自動生成)替換(已知 ID)

1.4 PUT 的完整替換語意

CAUTION

PUT 會完整替換資源,未提供的欄位會被清除!

javascript
// 原始資源
{
  "id": 123,
  "name": "John",
  "email": "john@example.com",
  "phone": "0912345678",
  "address": "台北市"
}

// PUT 請求(只傳 name 和 email)
PUT /api/users/123
{
  "name": "John Doe",
  "email": "johndoe@example.com"
}

// 結果:phone 和 address 被清除!
{
  "id": 123,
  "name": "John Doe",
  "email": "johndoe@example.com",
  "phone": null,      // 被清除
  "address": null     // 被清除
}

1.5 實作範例

javascript
// Express 後端
app.put("/api/users/:id", async (req, res) => {
  const { id } = req.params;
  const userData = req.body;

  // 完整替換(upsert 語意)
  const user = await User.findOneAndUpdate({ _id: id }, userData, {
    upsert: true, // 不存在則創建
    overwrite: true, // 完整替換
    new: true,
  });

  const statusCode = user.isNew ? 201 : 200;
  res.status(statusCode).json(user);
});

二、 PATCH 方法:部分更新資源

2.1 規範定義

根據 RFC 5789:

PATCH 方法請求對資源進行部分修改

簡單來說:PATCH 是「只更新我指定的那幾個欄位」。

2.2 PATCH 的特性

特性說明
安全性❌ 不安全(會修改資源)
冪等性⚠️ 取決於實作(通常非冪等)
快取❌ 不可快取

2.3 PATCH vs PUT

http
# 原始資源
{
  "id": 123,
  "name": "John",
  "email": "john@example.com",
  "phone": "0912345678"
}

# PUT:完整替換
PUT /api/users/123
{ "name": "John Doe" }
# 結果:{ id: 123, name: "John Doe", email: null, phone: null }

# PATCH:部分更新
PATCH /api/users/123
{ "name": "John Doe" }
# 結果:{ id: 123, name: "John Doe", email: "john@example.com", phone: "0912345678" }

2.4 為什麼 PATCH 可能非冪等?

假設有一個「增加計數」的 PATCH:

http
PATCH /api/articles/1
{ "operation": "increment_views" }
  • 第一次執行:views = 100 → 101
  • 第二次執行:views = 101 → 102
  • 結果不同,所以非冪等

但如果 PATCH 只是設定值:

http
PATCH /api/users/123
{ "name": "John" }
  • 多次執行結果相同,這種情況是冪等的

NOTE

PATCH 的冪等性取決於你的實作方式。設定固定值是冪等的,相對運算則不是。

2.5 JSON Patch 格式

除了直接傳修改後的值,還有一種標準格式 RFC 6902

http
PATCH /api/users/123 HTTP/1.1
Content-Type: application/json-patch+json

[
  { "op": "replace", "path": "/name", "value": "John Doe" },
  { "op": "add", "path": "/nickname", "value": "JD" },
  { "op": "remove", "path": "/phone" }
]

支援的操作:

操作說明
add新增屬性
remove移除屬性
replace替換值
move移動屬性
copy複製屬性
test測試值是否符合

2.6 實作範例

javascript
// Express 後端
app.patch("/api/users/:id", async (req, res) => {
  const { id } = req.params;
  const updates = req.body;

  // 只更新提供的欄位
  const user = await User.findByIdAndUpdate(
    id,
    { $set: updates }, // 使用 $set 進行部分更新
    { new: true }
  );

  if (!user) {
    return res.status(404).json({ error: "User not found" });
  }

  res.json(user);
});

三、 DELETE 方法:刪除資源

3.1 規範定義

根據 RFC 9110:

DELETE 方法請求伺服器刪除目標資源與其當前功能之間的關聯。

3.2 DELETE 的特性

特性說明
安全性❌ 不安全(會刪除資源)
冪等性✅ 冪等
快取❌ 不可快取

為什麼 DELETE 是冪等的?

DELETE /api/users/123  → 200 OK(用戶被刪除)
DELETE /api/users/123  → 404 Not Found(用戶已不存在)
DELETE /api/users/123  → 404 Not Found(還是不存在)

雖然回應可能不同,但伺服器最終狀態相同(用戶 123 不存在),所以是冪等的。

3.3 DELETE 的回應方式

http
# 選項 1:204 No Content(推薦)
DELETE /api/users/123 HTTP/1.1

HTTP/1.1 204 No Content

# 選項 2:200 OK + 確認訊息
DELETE /api/users/123 HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/json

{
  "message": "User deleted successfully",
  "deletedId": 123
}

3.4 軟刪除 vs 硬刪除

類型說明適用場景
硬刪除永久從資料庫移除測試資料、GDPR 要求
軟刪除標記為已刪除,不實際移除商業資料、可恢復需求
javascript
// 軟刪除實作
app.delete("/api/users/:id", async (req, res) => {
  const user = await User.findByIdAndUpdate(
    req.params.id,
    {
      deletedAt: new Date(),
      isActive: false,
    },
    { new: true }
  );

  res.status(204).send();
});

3.5 批量刪除

單個 DELETE 請求刪除多個資源的幾種方式:

http
# 方式 1:Query String
DELETE /api/users?ids=1,2,3,4,5

# 方式 2:Request Body(非標準但常見)
DELETE /api/users HTTP/1.1
Content-Type: application/json

{ "ids": [1, 2, 3, 4, 5] }

# 方式 3:使用 POST + 動作名稱
POST /api/users/batch-delete HTTP/1.1
Content-Type: application/json

{ "ids": [1, 2, 3, 4, 5] }

TIP

方式 3 雖然語意較不純粹,但避免了 DELETE + Body 的爭議,是較安全的選擇。


四、 OPTIONS 方法:詢問伺服器支援的操作

4.1 規範定義

根據 RFC 9110:

OPTIONS 方法請求描述目標資源的通訊選項

簡單來說:OPTIONS 是「這個 URL 支援哪些操作?」

4.2 OPTIONS 的特性

特性說明
安全性✅ 安全
冪等性✅ 冪等
快取✅ 可快取

4.3 主要用途:CORS 預檢請求

當瀏覽器要發送跨域請求時,會先發送 OPTIONS 請求確認伺服器是否允許:

4.4 OPTIONS 請求與回應範例

http
# 請求
OPTIONS /api/users HTTP/1.1
Host: api.example.com
Origin: https://client.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

# 回應
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://client.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

4.5 查詢支援的方法

http
# 請求
OPTIONS /api/users/123 HTTP/1.1
Host: api.example.com

# 回應
HTTP/1.1 200 OK
Allow: GET, PUT, PATCH, DELETE, OPTIONS

五、 HEAD 方法:只要標頭,不要內容

5.1 規範定義

根據 RFC 9110:

HEAD 方法與 GET 相同,但伺服器不得在回應中返回 Body。

簡單來說:HEAD 是「告訴我關於這個資源的資訊,但不用傳內容」。

5.2 HEAD 的特性

特性說明
安全性✅ 安全
冪等性✅ 冪等
快取✅ 可快取

5.3 主要用途

1. 檢查資源是否存在

http
HEAD /api/users/123 HTTP/1.1
Host: api.example.com

HTTP/1.1 200 OK          # 存在
HTTP/1.1 404 Not Found   # 不存在

2. 檢查資源是否更新(快取驗證)

http
HEAD /api/articles/42 HTTP/1.1
Host: api.example.com
If-None-Match: "abc123"

HTTP/1.1 304 Not Modified  # 沒變化,可用快取

3. 獲取檔案大小(下載前確認)

http
HEAD /files/large-video.mp4 HTTP/1.1
Host: cdn.example.com

HTTP/1.1 200 OK
Content-Length: 1073741824
Content-Type: video/mp4
Accept-Ranges: bytes

4. 檢查伺服器支援的功能

http
HEAD /api/files HTTP/1.1
Host: api.example.com

HTTP/1.1 200 OK
Accept-Ranges: bytes        # 支援範圍請求
X-RateLimit-Remaining: 99   # 剩餘配額

5.4 HEAD vs GET

GET /files/report.pdf
├── Headers(2KB)
└── Body(50MB)   ← 全部下載

HEAD /files/report.pdf
└── Headers(2KB) ← 只下載 Headers

節省頻寬,快速獲取資訊!


六、 方法總覽與選擇指南

6.1 完整比較表

方法用途安全冪等Body典型回應碼
GET獲取資源200, 304, 404
HEAD獲取標頭200, 304, 404
POST創建/處理201, 200, 400
PUT完整替換200, 201, 204
PATCH部分更新⚠️200, 204, 400
DELETE刪除資源⚠️200, 204, 404
OPTIONS查詢選項200, 204

6.2 RESTful API 設計範例

# 用戶資源 CRUD

GET    /api/users          → 列出所有用戶
GET    /api/users/123      → 獲取單一用戶
POST   /api/users          → 創建新用戶
PUT    /api/users/123      → 完整替換用戶
PATCH  /api/users/123      → 部分更新用戶
DELETE /api/users/123      → 刪除用戶
HEAD   /api/users/123      → 檢查用戶是否存在

6.3 決策流程圖


總結

方法一句話總結
PUT「用這個完整取代原本的」
PATCH「只改這幾個欄位」
DELETE「把這個刪掉」
OPTIONS「你支援哪些操作?」
HEAD「只告訴我資訊,不用傳內容」

> **PUT vs PATCH 記憶法**:

  • PUT = 整個放進去(replace)
  • PATCH = 貼補丁(修補部分)

進階挑戰

  1. 設計一個 API:用戶可以更新自己的頭像。你會用 PUT 還是 PATCH?如果只是更新頭像 URL 呢?如果是上傳檔案呢?
  2. 使用 cURL 對一個 API 發送 OPTIONS 請求,觀察 Allow 和 CORS 相關的 Headers。
  3. 實作軟刪除功能,並思考:被軟刪除的資源,GET 請求應該回傳什麼狀態碼?

延伸閱讀與資源