Image

夜已深,桌上的燈還亮著。你坐在螢幕前,滑著那些 commit 記錄,畫面一行一行閃爍著變更的痕跡。世界安靜下來時,反而讓人開始聽見自己的內心。

那是一段日子,你明明撐得很辛苦,卻還是對朋友說:「我很好。」
就像有的 API,明明出錯了,卻還是包裝成一個 200 OK。你開始意識到,有些錯誤不是程式的錯,而是我們不願意讓人知道「其實我現在沒那麼好」。
但 API 和人都一樣。你不說,別人只會以為你真的沒事。錯了,就該勇敢說出口。這不只是對外部的交代,更是對自己的一種體貼。



🧳 RESTful 派的觀點:讓 HTTP status 說話

WebAPI 出錯時,是否必須透過 HTTP Status 反映執行結果,例如:找不到時吐 404、系統出錯時回應 500?

在這個 API 世界裡,RESTful 就像是遵守交通號誌的好市民:綠燈行,紅燈停,該錯就錯,該成功就成功,乾淨俐落。


🎯 HTTP Status 是最基本的語言禮儀

RESTful 設計精神強調:HTTP Status Code 本來就是用來傳達狀態的。

  • 2xx:成功(200 OK、201 Created)
  • 4xx:客戶端出包(400 Bad Request、404 Not Found)
  • 5xx:伺服器出事(500 Internal Server Error)

這樣的分類看似老派,但其實好處多多:

  • Client 好判斷:前端不用解 body、比對內層 status,只要看到 4xx、5xx 就知道出事了。
  • SRE 好監控:非 2xx 就能被 ELK、Prometheus、Sentry 抓去泡茶討論,不用猜是不是回傳格式又包了什麼玄機。
  • 開發者少吵架:看到 500 沒有人會問「那這樣是成功還是失敗?」

🧑‍🎨 UI/UX 也愛它

想像一下,如果你網址 key 錯,API 回你 200,然後 body 寫著 “code”: “NotFound”,前端要怎麼辦?
沒錯──要嘛自己寫判斷邏輯,要嘛默默顯示個空畫面。

但如果回的是 404,前端可以設個全域攔截:

「找不到頁面,點我回首頁 👈」

這種 UX 等級提升,不靠神祕狀態碼,只靠 HTTP status 就能搞定,何樂不為?


🔐 資安與錯誤處理:不藏也不亂講

開發階段我們希望看到詳細錯誤(例如連 DB 失敗、NullReferenceException 等),
但上線之後,就該轉成一個簡單的 traceId 回傳給前端,背後配上完整的 log 留給內部查詢。

這不只是乾淨,更是保命:

  • 避免洩漏機敏錯誤資訊(例如連線字串、例外堆疊)
  • 符合弱點掃描規範(掃描工具看到 raw exception 會狂吼)
  • 配上 CI/CD 來避免環境變數搞錯、設定混亂,讓 API 更穩健。

🔧 營運排錯更順手

當你拿到一個 traceId,可以馬上去 ELK、AppInsights、Datadog 查 log,
再配合 F12 DevTools 抓 500 response,幾秒鐘就能定位是哪個 API 死給你看。

甚至還可以統計一週內 4xx/5xx 的出現比例,用來評估系統穩定度或團隊 KPI!
(這時候你會慶幸沒用那個萬能 200,把錯誤全部藏進地毯底下,這讓我想到勿言推理的那個司機,想想就毛骨悚然)


🧰 開發與介接工具天然支援

如果你照規矩使用 HTTP status,Swagger/OpenAPI 就會自動幫你生成正確的文件:

  • 200:正常資料長什麼樣
  • 400:驗證錯誤長什麼樣
  • 500:系統爆炸長什麼樣

但如果你所有錯誤都回 200,那你就要手動說明:「雖然 status 是 200,但請你一定要看 body 裡的 code 值喔,不然會以為成功」(然後下一位工程師就會大崩潰)


🗂️ Cache、框架與第三方工具也都照這套走

多數 cache 工具(如 Workbox)只會快取 200,如果錯誤也用 200,那錯誤畫面就會被 cache。多數前端框架(如 Axios、Retrofit)根據 HTTP status 自動分類成功/失敗,你用萬用 200 就是在逼他們多寫一次邏輯。



🤔 為什麼有人選擇「統一包成 200」?


1️⃣ 為了防爆網,大家都說自己沒事

有些系統是這麼設計的:

「只要我回 200,就不會觸發前端爆紅訊息,也不會讓使用者嚇到,Peace ✌️」

這種策略常見於:

UI 太脆弱,看到 500 就原地崩潰

一些第三方整合,只認得 status code 是 200 才願意處理資料

老舊系統改不了,靠 body 裡的 “code”: “E101” 來區分成功或失敗

這就像你不管點什麼,餐廳都回你「已送餐」,但送來的是空盤,還要你自己看盤底貼紙:「喔,原來是 ‘Out of stock’ 啊。」


2️⃣ 統一格式,逼大家都照 body 的規則走

有些團隊想要「格式一致性」,乾脆規定所有 API:

「無論發生什麼事,我都回 200,成功或失敗都藏在 response.body.code 裡,自己去判斷。」

這看似合理──但實際執行起來會發現:

前端 A 會用 if 判斷 “code”

前端 B 忘記判斷,直接把錯誤當成功顯示

前端 C 想做 retry,卻不知道這其實是 fatal error

然後你作為後端工程師,只能靜靜看著三個版本出現三種行為

這就像是學校規定「不管考什麼試都只發 A」,但老師會在試卷右下角註記「其實你不及格」,然後希望學生自己發現。



⚠️ 統一 200 帶來的副作用


🔍 監控工具看不到災情

ELK、Sentry、Prometheus 都愛聽 status code。你全都回 200,對這些監控工具來說,世界一片祥和,花都開好了,只有你自己在深夜默默重啟 service。


🔄 API 行為變得難預測

有些 API 回 200 是成功,有些回 200 是失敗,全靠 body 判斷。
你會開始聽到這種對話:

「欸這 API 是回 200 就算成功嗎?」
「要看,它是回 200,但裡面 code 要是 ‘0000’ 才算。」
「那哪支 API 不是?」
「…不知道,你看文件好了(雖然沒寫)。」


📄 文件工具無從支援

Swagger、Postman 想幫你產生 API 文件,卻不知道錯誤長怎樣。你全回 200,它只會幫你畫一個:

200 OK – 一切看起來都好像沒問題

但實際上裡面藏著 “code”: “E500”,然後你又要手動去補文件、補說明、補註解──人生突然變得很文書化。

🛠️ 要自己造太多輪子
像錯誤導頁、API 快取、重新嘗試(retry)、提示框設計……這些原本都可以靠 HTTP status 處理,現在通通要自己寫一套解析邏輯,保證工作量翻倍、bug 數翻三倍。


🔐 資安風險也不小

萬一你在 “message” 裡偷偷塞了 Exception message、stack trace,還真的全被前端顯示出去──恭喜你,不只用了錯的 status,還送出了一份免費的漏洞說明書給攻擊者。

其實最安全的做法是:只回一個 traceId,把細節寫進 log 裡就好,留給內部查。



☘️ 結語:別讓錯誤淹沒在成功之中

在那趟旅行的最後一天,你坐在窗邊,喝著那杯其實還不錯的熱巧克力,心裡忽然有些釋然。

你想到那些沒說出口的「我其實不好」,那些錯誤被靜靜包成 200 的回應,還有那些被誤以為成功、實則在等待被理解的訊號。

程式世界也是如此。

回傳 404,並不代表我們失敗,只是誠實地說出:「我現在找不到。」
回傳 500,不是崩潰,而是請求:「我需要一點時間,請你等等我。」

這比默默回個 200 然後什麼都沒發生來得有力量,也更值得被信任。

我們在設計系統的同時,其實也在學著怎麼好好地對話——對使用者、對隊友,也對自己。

所以別讓錯誤藏在看似一切正常的外殼裡。勇敢讓它們說話,
因為一個願意說實話的 API,就像一個願意承認脆弱的大人,
雖然不完美,卻可靠且溫柔。