題目與情境
API 為文件回傳版本驗證器,多個用戶端可以並行讀寫;要求拒絕過期寫入,同時維持介面可重試且可觀測。
面試官考察什麼
- 區分快取表示與寫入前置條件。
- 選擇強 ETag,並在變更方法上執行
If-Match。 - 過期版本回傳 412,並定義安全的用戶端恢復路徑。
作答前的釐清問題
- ETag 代表精確儲存表示,還是只代表弱語意版本?
- PUT、PATCH、DELETE,還是所有寫操作都必須帶前置條件?
- 用戶端是否自動合併欄位,還是必須由使用者解決衝突?
- 重試是否經過負載平衡 API 和同一個交易型資料儲存?
30 秒回答框架
我會在每個可編輯表示上回傳強 ETag。用戶端在 PUT、PATCH 或 DELETE 中傳送 If-Match。伺服器在同一交易內比較它並執行更新;版本不相符就回傳 412,不產生副作用。回應可提供目前表示或重新取得訊號,同時記錄衝突與缺少前置條件的指標。用戶端重新取得、明確合併,再用新 ETag 重試。
分步深入
1. 產生驗證器
GET 回傳文件及由規範化儲存版本產生的 ETag。資料庫修訂號通常比對大型載荷雜湊更簡單,只要相關表示每次變化都會更新。If-Match 保護精確寫入狀態,應使用強驗證器。
2. 原子執行前置條件
更新必須在同一個條件操作中檢查期望版本並寫入新版本。概念 SQL 如下:
UPDATE documents
SET body = :new_body, version = version + 1
WHERE id = :id AND version = :expected_version;影響行數為零時回傳 412,不執行副作用。先在應用程式內檢查 ETag、稍後再寫入會留下檢查與提交之間的競態。
3. 區分 412 與其他失敗
412 表示提供的前置條件為假,是並行衝突;不是 JSON 格式錯誤,也不是驗證失敗。請求結構無效用 400,驗證或權限問題用 401/403,資源按介面策略不可用時用 404。如此用戶端才能選擇重新取得合併,而非統一重試。
4. 設計用戶端恢復
收到 412 後取得目前文件,展示衝突欄位或差異。只有欄位語意和權限規則允許時才自動合併。重試必須使用新回傳的 ETag,並維持資源層級的冪等性;不要盲目重放舊請求。
5. 維持快取與副本一致
驗證器應來自寫入路徑可見的已提交狀態。落後副本可能回傳舊 ETag,造成不必要衝突;寫請求轉到其他節點時,仍必須由主交易執行版本檢查。記錄衝突率、缺少 If-Match 比率和重試成功率。
高品質示範回答
「我會為每份可編輯文件回傳強 ETag,並要求變更請求帶 If-Match。伺服器在更新資料列的同一交易中比較版本;沒有匹配資料列就回傳 412,且不執行副作用。用戶端重新取得文件,展示差異或按明確規則合併,再用新 ETag 重試。我會把 412 與驗證、權限錯誤分開,並監控舊讀、衝突和重試成功率。」
常見誤區
- 先檢查 ETag 再分開更新 → 仍存在競態 → 在一個原子版本條件更新中完成。
- 精確寫入使用弱驗證器 → 比較可能失真 → 使用強驗證器。
- 所有過期寫入都回傳 409 → 用戶端無法識別協定前置條件 → If-Match 失敗使用 412。
- 盲目重試舊載荷 → 可能覆蓋第一次修改 → 重新取得、明確合併,再用新標籤重試。
追問與回答
ETag 只能用於快取嗎?
不能。If-None-Match 常用於快取校驗,If-Match 則讓寫入依賴目前表示。只要比較強度和產生策略正確,同一驗證器可以承擔兩種用途。
用戶端不傳送 If-Match 怎麼辦?
若資源要求樂觀並行,應按契約拒絕,回傳明確的前置條件要求或 400 策略。靜默接受無條件寫入會重新引入遺失更新;各方法必須保持一致。
412 回應應回傳最新文件嗎?
在權限和載荷大小允許時可以回傳,但契約仍應要求重新取得或明確解決衝突。回傳資料不代表用戶端可以不帶最新驗證器覆蓋它。