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
| 面向 | POST | PUT |
|---|---|---|
| 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: 864004.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: bytes4. 檢查伺服器支援的功能
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 = 貼補丁(修補部分)
進階挑戰
- 設計一個 API:用戶可以更新自己的頭像。你會用 PUT 還是 PATCH?如果只是更新頭像 URL 呢?如果是上傳檔案呢?
- 使用 cURL 對一個 API 發送 OPTIONS 請求,觀察
Allow和 CORS 相關的 Headers。 - 實作軟刪除功能,並思考:被軟刪除的資源,GET 請求應該回傳什麼狀態碼?