API 什麼時候該回傳 204 No Content?
題幹與適用場景
請設計 REST API 的空回應契約:成功刪除資源後如何回應?集合查詢沒有符合項目時是否回傳 204?更新成功但不需回傳表示時如何選擇?要區分資源不存在、操作成功但無表示、非同步處理中與合法空集合。假設客戶端使用多種語言,契約需長期相容。
面試官考察點
強回答把狀態碼當作資源語意,而非「有沒有資料」的快捷開關。204 表示請求成功且沒有訊息內容,回應不能帶訊息 body;200 表示成功並可回傳穩定表示,例如 [];404 表示目標資源不存在或沒有目前表示。也要考慮 DELETE 冪等性、快取、SDK 解碼與 OpenAPI 文件。
回答前需要釐清的問題
- 操作目標是什麼?刪除單一資源、更新資源與查詢集合的語意不同。
- 空集合是正常結果嗎?若是,200 加空陣列通常比 204 更能保持回應型別穩定。
- 客戶端是否必須解碼統一 JSON?若 SDK 總是讀取 body,204 可能觸發意外 EOF,需要明確分支。
- 成功後是否需要新資源表示、ETag 或非同步任務 ID?需要時不應丟棄內容,應選 200、201 或 202。
推薦解法與推導
建立按操作和表示需求劃分的契約:
DELETE /users/42成功且沒有要回傳的表示,可用 204;重複刪除若業務視為冪等成功,也可繼續 204,但需在文件固定。GET /users?team=none若集合存在但沒有成員,回傳 200 與[],保持列表型別穩定;不能把零行誤判成資源不存在。GET /users/42找不到目標資源回傳 404;這是目標資源語意,不是空列表語意。PUT /users/42成功且客戶端需要更新後表示,用 200 與資源 JSON;成功但不回傳表示,可用 204,並以 ETag 等 response header 提供元資料。- 接受請求但背景仍處理,用 202 與任務狀態連結,不要偽裝成 204。
HTTP/1.1 204 No Content
ETag: "user-42-v7"
Cache-Control: no-store
HTTP/1.1 200 OK
Content-Type: application/json
[]RFC 9110 明確 204 不包含訊息內容;因此客戶端、代理與測試都應以無 body 為契約。不要為了統一 HTTP 200 把業務錯誤塞進 200,也不要為了省幾個位元組讓每個空結果都變成 204。
替代方案與取捨
200 空陣列的優點是型別穩定、生成 SDK 容易處理,列表分頁與元資料也可繼續回傳;代價是多幾個位元組。204 明確表達成功且無表示,適合 DELETE 或不需回顯的更新;代價是客戶端必須處理無 body。404 應保留給目標資源不存在,不能表示合法空集合。
失敗場景、邊界與反例
GET空列表回傳 204,讓客戶端把空結果當成另一種型別,破壞分頁與泛型解碼。- 204 仍傳送 JSON body;標準語意禁止訊息內容,代理可能丟棄或客戶端行為不一致。
- DELETE 第一次 204、第二次 404 卻未說明冪等策略,重試會產生不必要錯誤。
- 用 200
{ "error": ... }表示失敗,監控和 SDK 會把業務錯誤當成功。 - 更新後需要新 ETag 或版本卻回傳 204 且不提供 response header,客戶端無法安全快取或控制並發。
測試與驗證清單
為每個端點寫狀態碼、body、Content-Type、ETag 與快取 header 契約測試。覆蓋第一次與重複 DELETE、空集合、缺失單體資源、成功更新有無表示、202 非同步分支、代理轉發與 SDK 解碼。用 OpenAPI 產生至少一種客戶端,驗證 204 不觸發 JSON 解析錯誤;同時檢查監控正確分組 2xx、404 與業務錯誤欄位。
追問與延伸
204 能否帶 ETag 或其他 response header?
可以帶 header;禁止訊息內容不等於禁止元資料。ETag、快取控制或追蹤 ID 可幫助並發控制和診斷,但要在契約說明存在條件。
空分頁回傳 200 還是 204?
若端點表示型別是列表,優先 200 加空陣列並保留分頁元資料。只有端點明確把成功但無表示作為語意,且所有客戶端能處理無 body 時,才考慮 204。
DELETE 找不到資源時一定要 404 嗎?
不一定。若 API 把刪除定義為冪等的確保資源不存在,重複請求可回傳 204;若呼叫者需要知道目標曾否存在,則回傳 404。選擇應穩定記錄在文件、SDK 與監控。