具代表性的面試主題

後端面試:什麼時候應該回傳 HTTP 202 Accepted?

後端中等
Offer.cc 編輯團隊發佈 更新

題幹

一個 POST 會啟動可能超過請求逾時的工作。什麼時候回傳 202 Accepted?回應要包含什麼?如何讓重試與失敗可被觀察?

題目與情境

你負責 POST /exports 大型匯出 API。請求會驗證參數並啟動工作,但匯出可能耗時數分鐘。面試官要求你在 200201202 之間選擇,並說明客戶端如何知道最終結果。

假設伺服器能持久化匯出記錄並投遞工作。客戶端可能逾時後重送請求。回答必須定義完整合約,不能只報一個狀態碼。

面試官在考察什麼

  • 能否區分「資源已建立」與「請求已接受、稍後處理」。
  • 能否建模持久狀態資源、終態與錯誤詳情。
  • 重試是否會建立兩個匯出工作,或遺失原始回應。
  • 資料庫更新與佇列投遞在沒有分散式交易時是否仍可靠。

作答前的釐清問題

  1. POST 是否立即建立持久匯出資源?若是,201 Created 可以描述該資源;否則 202 可以確認工作已接受。
  2. 同一個邏輯請求是否允許重試?若允許,應要求冪等鍵或呼叫端操作 ID。
  3. 客戶端需要輪詢、Webhook,還是兩者都要?這會改變狀態表示與通知合約。
  4. 匯出檔案的保留與授權規則是什麼?工作完成不代表任何人都能下載結果。

30 秒回答框架

「只有在處理被延後且最終結果尚未準備好時,我才回傳 202 Accepted。我先建立持久工作記錄,再回傳狀態 URI 與操作識別。客戶端使用退避輪詢,或接收經過驗證的回呼。冪等鍵把重試映射到同一個工作與回應。工作經過排隊、執行、成功或失敗等明確狀態;worker 與 outbox 都可重試,狀態 API 仍是事實來源。」

分步深入解答

1. 依資源生命週期選擇狀態碼

200 OK 表示請求已完成並回傳表示。201 Created 表示資源已建立且可被識別。202 Accepted 表示請求已接受,處理可能尚未開始或尚未完成;它不承諾最後成功。

若匯出記錄同步建立,且它就是呼叫端要管理的資源,可以回傳 201 與該資源。若 API 只是確認背景工作已接受,則用 202 加監控 URI 更清楚。選擇應由可觀察生命週期決定,不是因為系統內部用了佇列。

2. 讓回應可以執行

回傳操作 ID、狀態 URL,以及包含 state、時間戳與安全重試提示的表示。最小回應可以是:

http
HTTP/1.1 202 Accepted
Location: /exports/exp_123
Retry-After: 5
Content-Type: application/json

{"id":"exp_123","state":"queued","status_url":"/exports/exp_123"}

每次讀取狀態資源都要授權。queuedrunning 是非終態;succeeded 回傳短期下載引用;failed 回傳穩定錯誤碼與修正提示,不能洩露堆疊。客戶端也要能處理超過保留期後資源消失。

3. 讓重試收斂

要求建立工作的操作帶上 Idempotency-Key。保存相關請求的雜湊、工作 ID 與回應狀態。相同鍵搭配相同請求時回傳原結果;相同鍵搭配不同請求時回傳客戶端錯誤。不能只依賴時間視窗,因為遲到的重試可能在視窗後抵達。

不同鍵仍可以建立兩個匯出工作。冪等性防止一個邏輯操作重複建立,不代表 worker 能做到恰好執行一次。

4. 關閉資料庫到佇列的間隙

在一個資料庫交易中同時寫入匯出記錄與 outbox 事件。relay 發布待處理 outbox,並在 broker 確認後標記已送出。崩潰可能造成事件重複發布,因此消費者以匯出 ID 作為冪等鍵。這保證已提交的工作最終可被發現,但不宣稱資料庫與 broker 具備原子提交。

worker 以條件狀態轉換更新工作,例如 queued -> running -> succeeded|failed。過期重試不能把 succeeded 改回 running。指標應包括佇列等待時間、執行時間、終態失敗率與 outbox 延遲。

5. 定義輪詢、回呼與取消

狀態 API 提供 ETag 或版本號,讓輪詢使用條件請求。客戶端依伺服器提示做指數退避,並在終態停止輪詢。Webhook 是最佳化,不是唯一結果通道:投遞可能失敗,因此客戶端必須能讀取狀態資源進行對帳。

取消使用獨立命令,例如 POST /exports/exp_123/cancel。只允許可取消狀態,而且取消本身也要冪等。工作已 succeeded 後,遲到的取消不能回滾結果。

高品質示範回答

我會先確認這個呼叫是否同步建立持久匯出資源。若是,可以回傳 201 與資源記錄。對於結果延後、回應只確認接受的操作,我回傳 202,附帶操作 ID 和需要授權的狀態 URL。我要求冪等鍵,保存請求指紋和工作 ID,讓重試得到同一表示。

交易同時寫入匯出記錄和 outbox 事件,relay 與冪等消費者按至少一次投遞處理。狀態機單調地從排隊到執行,再到成功或失敗。客戶端用條件請求和退避輪詢,Webhook 只是加速器。我會監控佇列時間、outbox 延遲和終態錯誤,並明確保留、授權、下載連結過期和取消規則。202 只確認接受,不確認成功。

常見失誤

  • 錯誤表現:202 當成工作一定成功 → 失敗原因:語義允許處理失敗或根本未開始 → 修正方法:暴露終態失敗與保留策略。
  • 錯誤表現:只回傳 202,不給狀態 URL → 失敗原因:客戶端無法發現狀態,只能猜測 → 修正方法:回傳受授權保護的狀態資源與操作 ID。
  • 錯誤表現:資料庫提交後再直接發布佇列訊息 → 失敗原因:崩潰可能留下無人處理的工作 → 修正方法:使用交易 outbox 與可重播 relay。
  • 錯誤表現:認為佇列能保證恰好執行一次 → 失敗原因:投遞重試與崩潰會造成重複 → 修正方法:消費者冪等,狀態轉換帶條件。
  • 錯誤表現:相同冪等鍵無條件回傳成功 → 失敗原因:它可能掩蓋請求已被修改 → 修正方法:比較請求指紋,衝突時拒絕。

追問與回答

這裡應該改用 201 嗎?

當同步呼叫建立持久匯出資源並能透過 Location 識別它時回傳 201。當有意義的結果被延後、回應只確認接受時回傳 202。有些 API 可以用 201 建立工作資源,同時把資源狀態設為 pending;必須說明狀態描述的是哪個資源。

如果客戶端永遠不輪詢怎麼辦?

在約定保留期內持久保存狀態,傳送可選的驗證 Webhook,並允許客戶端之後用操作 ID GET。回呼失敗不能刪除唯一的狀態路徑。客戶端幾天後回來時,仍要執行下載連結過期和授權檢查。

worker 可以兩次更新同一個工作嗎?

可以,投遞通常是至少一次。使用唯一匯出 ID、帶條件的狀態轉換和冪等輸出寫入。重複的 succeeded 事件應該無害;終態回到 running 的轉換應被拒絕並記錄。

公開來源

同類題目