【新手指南】API 回傳 200 OK 卻給 `{ error: failed }`?別成為前端眼中的「雷包」!5 個讓資深工程師點頭的 RESTful 設計心法
本文由資深軟體架構師視角出發,深入解析為何「HTTP 200 OK 夾帶錯誤訊息」是破壞通訊協定語意的反模式。我們將回歸計算機科學的第一原理,探討 REST 架構中的狀態碼語意、冪等性設計與 RFC 7807 錯誤處理標準,提供 5 個核心設計心法,協助新手工程師建構出強健、可擴展且符合工業標準的 API 系統。
身為一名在分散式系統領域打滾多年的架構師,我常在 Code Review 環節看到一種令人嘆息的現象:一個 REST API 的 HTTP Status Code 回傳 200 OK,但 Response Body 卻寫著 {"status": "error", "message": "Invalid parameters"}。
這在初學者眼中或許只是「風格不同」,但在系統工程的視角下,這是對 通訊協定語意(Protocol Semantics) 的嚴重破壞。HTTP 協定不僅僅是傳輸數據的管道,它本身就是一種應用層的狀態機。當你破壞了這層語意,你就破壞了整個網際網路基礎設施(如 CDN、Load Balancer、瀏覽器快取)對你系統的理解能力。
今天,我們不談抽象的哲學,我們從工程原理出發,探討 5 個讓資深工程師點頭的 RESTful API 設計心法。
1. 尊重協定:狀態碼是控制流的基石 (Status Codes as Control Flow)
HTTP 狀態碼是伺服器與客戶端溝通的「第一層握手」。
- 2xx (Success):請求已成功被伺服器接收、理解並接受。
- 4xx (Client Error):客戶端發生錯誤(如參數錯誤、權限不足)。這意味著「你不修改請求,重試幾次都不會成功」。
- 5xx (Server Error):伺服器端發生錯誤。這意味著「現在是我的錯,你稍後重試可能會成功」。
當你發生錯誤卻回傳 200 OK,你強迫客戶端必須解析 Body 才能知道請求失敗。這增加了客戶端的解析成本(Parsing Overhead),且讓監控系統失效——你的 Load Balancer 看到全是 200,卻不知道背後的業務邏輯早已崩潰。請精準使用 400 Bad Request、401 Unauthorized、403 Forbidden 與 404 Not Found。
2. 定義錯誤的形狀:RFC 7807 標準 (Standardized Error Payloads)
隨意回傳字串 {"error": "出錯了"} 是不夠的。在大型系統中,我們需要結構化的錯誤訊息。我強烈建議採用 IETF RFC 7807 (Problem Details for HTTP APIs) 標準。
一個標準的錯誤回應應該長這樣:
{
"type": "https://api.example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"status": 403,
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc"
}
這不僅是規範,更是一種「類型系統」的延伸,讓機器(Machine-readable)能夠自動化處理錯誤,而非依賴人工閱讀字串。
3. 理解動詞的代價:冪等性 (Idempotency)
在分散式系統中,網路是不可靠的。封包會丟失,請求會超時。這時,「重試(Retry)」機制至關重要。但什麼時候可以安全重試?這取決於你對 HTTP 動詞的選擇。
- GET:讀取資源。這是安全(Safe)且冪等(Idempotent)的。無論呼叫多少次,伺服器狀態不變。
- PUT:替換資源。這是冪等的。如果你發送「把 A 改為 B」十次,結果 A 依然是 B。
- POST:新增資源或處理數據。這通常不是冪等的。發送十次,可能會建立十筆訂單。
新手常犯的錯誤是用 POST 來做查詢,或者用 GET 來修改資料。這不僅違反語意,更會導致快取中毒(Cache Poisoning)或重複交易的嚴重後果。
4. 名詞導向的資源設計 (Noun-Oriented Resource Naming)
RPC (Remote Procedure Call) 風格的 API 喜歡用動詞,如 /createNewUser 或 /deleteProduct。但在 REST 架構中,URI (Uniform Resource Identifier) 應該是用來識別「資源(Resource)」的名詞。
-
Bad:
POST /api/createArticle -
Good:
POST /api/articles -
Bad:
POST /api/article/123/publish -
Good:
PUT /api/articles/123(Body 包含{"state": "published"})
這種設計思維將系統視為一組「狀態」的集合,而非一組「函數」的集合,這更符合 Web 的超媒體(Hypermedia)本質,也更容易進行擴展與版本管理。
5. 顯式的版本控制 (Explicit Versioning)
軟體會演進,API 契約(Contract)會改變。永遠不要假設你的 API 只有一個版本。當你需要做出破壞性變更(Breaking Change)時,必須有明確的遷移路徑。
常見策略有二:
- URI Versioning:
/v1/articles。優點是直觀,瀏覽器可探索;缺點是破壞了資源的唯一性(同一個資源有兩個 URL)。 - Header Versioning:
Accept: application/vnd.myapi.v1+json。這更符合 HTTP 語意(Content Negotiation),但測試與除錯較為繁瑣。
無論選擇哪種,重點是「顯式(Explicit)」。不要讓客戶端猜測他們正在與哪個版本的邏輯互動。
總結 (Critique)
REST 不是唯一的架構風格(GraphQL 和 gRPC 在特定場景下有其優勢),但它依然是網際網路的通用語言。遵循這些原則,你不僅是在寫程式,更是在構建一個符合 Web 架構原理、可擴展且強健的通訊系統。別為了圖一時方便而犧牲了架構的優雅與正確性,那是資深工程師與「碼農」的分水嶺。
🛠️ CULTIVATE Recommended Tools | 精選工具推薦
- Interactive Brokers: Low cost professional trading platform for global markets.
- Poe: Access all top AI models (GPT-4, Claude 3, Gemini) in one place.
Disclosure: CULTIVATE may earn a commission if you purchase through these links.