題幹與適用場景
一個線上 API 的資源路徑要永久從 /v1/orders 遷移到 /v2/orders。呼叫方既有瀏覽器表單,也有行動端、第三方 SDK 和非同步工作;請求可能是 POST,帶有較大的 JSON 請求本文和冪等鍵。請設計遷移期間的狀態碼、用戶端行為、觀測指標和回滾路徑。
這道題考查 HTTP 重新導向語意是否被準確理解,以及 API 遷移能否避免重複副作用。RFC 9110 定義了 308 Permanent Redirect;MDN 對 308 的說明強調,用戶端在新位址重送時不應修改原請求方法和請求本文。301 是永久重新導向,但歷史用戶端在非 GET 請求上的處理存在方法改變的相容差異。
面試官考察點
面試官會觀察你是否先區分永久與暫時遷移,再區分是否必須保留方法和請求本文。強回答會比較 301、302、307、308,說明為什麼帶副作用的 POST 不能依賴用戶端「猜測」重試,並把冪等鍵、驗證、逾時和回滾納入方案。
還要檢查你是否考慮 Location 的可信範圍、跨域憑據、快取傳播、SDK 的重新導向上限,以及舊用戶端不支援 308 時的相容策略。只背「308 等於 301 加保留 POST」而沒有遷移驗證,仍然不完整。
回答前需要澄清的問題
遷移的永久性和範圍
確認新位址是否已經穩定,以及是否只遷移單個資源、整個 API 前綴還是跨域。若目標仍可能變更,先用 307 表達暫時遷移,避免把永久快取語意過早寫入用戶端和中間層。
用戶端能力與副作用
列出瀏覽器、行動端版本、SDK、佇列消費者和合作方。確認 POST 是否會建立訂單、扣款或傳送訊息,以及呼叫方是否傳送冪等鍵;無法證明安全重放時,不能把重新導向當作無條件重試。
憑據、快取與回滾
確認新舊網域的驗證範圍、CORS、代理和 CDN 行為。詢問是否允許舊端點繼續服務,以及回滾是撤銷重新導向、切換路由,還是恢復舊版本處理器。
30 秒回答框架
「我先確認遷移是永久還是暫時,並盤點所有用戶端。對必須保留 POST 方法和請求本文的永久遷移,我會優先使用 308;暫時遷移用 307。301 不適合作為依賴用戶端保留非 GET 語意的契約。舊端點先雙寫或代理到新處理器,所有副作用使用同一個冪等鍵,並限制 Location 的網域和憑據轉送。灰度期間監控 3xx 跟隨率、重複建立、4xx/5xx、請求本文大小和 SDK 版本;異常時撤銷重新導向並保留舊端點處理能力。」
分步驟深入解答
第一步:準確選擇狀態碼
308 表示永久遷移,重新導向後的請求方法和請求本文保持不變;307 表示暫時遷移,也保持方法和請求本文。301 表示永久遷移,但歷史用戶端對 POST 等方法的處理並不都保持原方法,不能把它當作嚴格的 POST 遷移契約。302 也不應承擔這種保證。
第二步:把重放風險當成核心約束
308 可能讓用戶端再次提交完整請求本文,因此新舊端點都要按相同冪等鍵識別同一次業務命令。伺服器在真正執行副作用前驗證鍵與請求摘要;同一鍵收到不同參數時回傳衝突,而不是再次建立訂單。逾時後的用戶端仍需遵守 SDK 的重試預算,不能因為看到 308 就無限跟隨。
第三步:分階段發布與觀測
先讓新位址直接可用,再讓舊位址以可觀測的代理或 308 回應導流。按 SDK 版本、來源網域和介面方法做小比例灰度,記錄重新導向鏈長度、跟隨失敗、重複副作用、目標端延遲、驗證失敗和請求本文大小。對不支援 308 的舊用戶端,可以短期保留舊端點的伺服器代理,不要靜默改成 301 並假設其行為一致。
第四步:處理安全、快取和回滾
Location 只能指向經過允許清單驗證的目標,跨域跳轉前重新評估 Cookie、Authorization 和 CORS,避免把憑據帶到不受信任的網域。明確 CDN 和用戶端快取的生效範圍,先縮短驗證窗口,再逐步延長。回滾時先停止傳送新重新導向,舊端點繼續接受相同冪等鍵;已經寫入新端點的結果不能透過簡單切回舊路由而「撤銷」。
高品質示範回答
我會把它當成協定和遷移題,而不是只選一個數字。永久遷移且必須保留 POST 方法和請求本文時選擇 308;暫時切流選擇 307。301 的永久語意適合很多頁面位址,但不能給非 GET API 提供我需要的嚴格方法保留保證。
上線前我讓新舊位址都支援同一份驗證、請求校驗和冪等鍵協定。舊端點先代理到新處理器並保留完整日誌,確保相同鍵和相同請求摘要只產生一次副作用。隨後按用戶端版本灰度回傳 308,限制目標網域,重新檢查跨域憑據,並驗證 CDN、SDK 和佇列消費者的跟隨行為。
監控重新導向跟隨率、鏈路長度、重複建立、目標端錯誤率、驗證失敗和請求本文大小。若舊用戶端不識別 308,就繼續使用舊端點代理,不能偷偷降級成 301。異常時停止 308、保留舊端點和冪等記錄,恢復路由後再分析已完成的業務結果。
常見錯誤
- 所有遷移都回傳 301 → 歷史用戶端可能改變 POST 方法或遺失請求本文 → 對必須保留方法和請求本文的永久 API 遷移使用 308,並驗證用戶端。
- 看到 308 就再次執行副作用 → 重新導向、逾時和用戶端重試可能疊加建立請求 → 使用冪等鍵、請求摘要和有限重試預算。
- 把 307 當成永久位址 → 暫時狀態被長期快取或寫進 SDK 設定,後續回滾困難 → 暫時切流使用 307,穩定後再決定是否轉為 308。
- 忽略 Location 的跨域風險 → Cookie 或 Authorization 可能被帶到不受信任目標 → 目標網域允許清單、憑據策略和 CORS 一起驗收。
- 只看 3xx 數量 → 不能發現舊 SDK 跟隨失敗或重複業務寫入 → 按用戶端版本關聯跟隨率、錯誤和副作用結果。
追問及應對
追問一:為什麼不直接讓舊位址回傳 200 並在回應本文提示新位址?
這不會讓通用用戶端、快取和 SDK 自動遷移,也無法表達資源已經永久移動。可以在過渡期保留舊端點代理,但遷移契約仍應透過明確的狀態碼和 Location 表達,並記錄呼叫方是否真正切換。
追問二:308 到新網域時能否原樣轉送 Authorization?
不能預設轉送。先判斷兩個網域是否屬於同一受信任邊界,再讓用戶端重新取得或明確攜帶目標網域憑據。伺服器應拒絕任意使用者可控的 Location,避免開放重新導向和憑據外洩。
追問三:舊用戶端完全不支援 308 怎麼辦?
保留舊端點的伺服器代理或按用戶端能力回傳相容回應,直到舊版本退出。代理必須重用請求冪等鍵並設定截止日期;不能在沒有資料的情況下聲稱所有用戶端都會把 308 當成 301 處理。
追問四:回滾是否只需把 308 改回 200?
還要確認新端點已經寫入的訂單、事件和稽核記錄。先停止新增重新導向並恢復舊端點入口,再讓兩端讀取同一權威狀態,避免重複寫入或出現新舊版本分叉。回滾完成後透過請求 ID 和冪等鍵核對已完成副作用。