題幹與適用場景
一個公共 API 需要增加 Features 回應欄位,返回能力名稱、優先級和實驗參數。客戶端、CDN、閘道與多種語言的 SDK 都會讀取它;舊客戶端只能忽略未知欄位。請設計欄位格式、傳送與解析規則、相容策略及驗證方案。
這是一道後端 API 契約題,核心是把「看起來像字串的標頭」變成可互通的協定。RFC 8941 定義 Item、List、Dictionary 及參數的通用模型,RFC 9651 是後續修訂;RFC 9110 要求新欄位明確語法,並防止控制字元和錯誤的多值合併。答案不要求實作完整 RFC 解析器,但必須說明邊界。
面試官考察點
面試官會觀察你是否先定義欄位語意,再選擇 List、Dictionary 或 Item,而不是直接拼接逗號字串。強回答會說明傳送方序列化、接收方嚴格解析、未知成員處理、重複欄位合併、大小上限和觀測指標。
普通回答只展示一個示例值;高品質回答能解釋為什麼能力集合適合 Dictionary、為什麼參數鍵必須小寫、為什麼非 ASCII 文字不應偷偷塞進 String,以及解析失敗時如何安全降級而不誤開實驗功能。API 面試資料也把穩定契約、向後相容和失敗處理視為核心評分點。
回答前需要澄清的問題
欄位語意與可信邊界
確認該欄位是提示、授權還是業務事實。若它影響權限、計費或安全策略,不能只依賴客戶端回傳;伺服器必須保留權威狀態。也要問欄位是否允許快取,以及不同使用者的值是否不同。
型別與相容範圍
確認值是無序能力集合、需保持順序的優先級清單,還是單一版本標識。詢問舊客戶端是否必須繼續工作、是否允許新增參數、閘道是否會合併重複欄位。答案會決定選擇 Dictionary、List 或單一 Item,以及未知參數的處理規則。
失敗與容量預算
確認非法欄位是忽略整個欄位、忽略單一成員還是拒絕回應。設定欄位位元組上限、成員數量上限和解析耗時預算;這些約束會影響拒絕策略與 DoS 防護。
30 秒回答框架
「我先把欄位定義成可版本化的機器協定,而不是把 JSON 藏在字串裡。能力集合用 Dictionary,每個鍵的值是布林值或帶參數的 Item;鍵和參數按 RFC 規則序列化,未知成員預設忽略。伺服器只傳送 ASCII、限制總長度和成員數,接收方使用同一套規範解析,遇到非法語法就按欄位缺失處理,不把猜測結果當成已啟用。閘道要測試重複欄位合併和快取變化,發布時先影子解析,再比較解析失敗率、欄位大小和功能誤開率。」
分步驟深入解答
第一步:先建模再選容器
如果只表達能力開關,Dictionary 能把鍵映射到布林值,例如 search;v=2, upload=?1。如果每項需要順序和參數,List 更合適。單一版本或策略名稱才使用 Item。不要為了方便把結構化值編成 JSON,因為代理和不同語言 SDK 仍會面對自訂解析器。
第二步:定義可擴充的欄位
假設回應欄位是能力字典,定義 search、upload 等鍵;參數如 v=2、tier="pro" 只承載協定允許的型別。參數鍵使用小寫,非 ASCII 產品名稱放在獨立回應體或明確的 Display String 擴充中。把每個成員的語意、預設值和非法值後果寫進契約,而不是依賴實作猜測。
Features: search;v=2, upload=?1傳送方必須穩定序列化:同一個抽象值應產生可預測的位元組表示,避免快取鍵和簽名輸入因空格或參數順序產生無意義差異。接收方不能用普通字串 split 代替語法解析,因為逗號、括號、引號和參數邊界有明確規則。
第三步:規定重複欄位與未知成員
先確認欄位是否允許多行。若定義為 Dictionary,多個欄位行可能被中介軟體合併,伺服器應在規範中聲明合併後語意,並拒絕會產生衝突的重複鍵。未知鍵和未知參數預設忽略,已知鍵的非法型別則忽略該成員或整個欄位,具體選擇要寫死。安全開關不應因解析失敗而預設開啟。
第四步:建立嚴格但可用的解析邊界
解析前限制總位元組數、成員數、巢狀深度和 CPU 時間;拒絕 CR、LF、NUL 及超出欄位語法的控制字元。對外部輸入不必追求常量時間,但要避免例外路徑把大欄位反覆重解析。記錄失敗原因類別,不記錄可能含敏感值的完整欄位。
第五步:處理快取、簽名與演進
如果欄位因使用者或實驗分組變化,回應必須帶正確的 Vary 或使用私有快取,否則 CDN 會把一個使用者的能力暴露給另一個使用者。若欄位參加 HTTP Message Signatures,簽名方和驗簽方必須對結構化值採用同一規範化規則。新增鍵和可選參數應保持舊客戶端可忽略;移除或改變既有語意需要新版本或遷移窗口。
第六步:用灰度與反例證明方案
先在影子模式解析但不改變業務,再對少量內部客戶端啟用。構造重複鍵、空清單、錯誤引號、未知參數、超長欄位、代理合併和快取錯配樣例。比較有無欄位時的解析成功率、誤開率、回應體積、CPU、快取命中和 SDK 版本分布。任何解析失敗都應回到安全預設值。
高品質示範回答
我會把它當成協定設計題。先問欄位是否只是能力提示;如果它決定權限,我會把權威判斷留在伺服器。對於一組能力,我選擇 Structured Field Dictionary,並為每個鍵定義布林值或帶參數的 Item;順序不重要的能力不使用 List。規範明確序列化、參數型別、重複欄位、未知成員和非法值後果。
傳送方只產生受限 ASCII,並限制欄位大小和成員數量。接收方使用 RFC 相容解析器,不用逗號切分;它拒絕控制字元,未知鍵忽略,已知鍵型別錯誤按缺失處理。多個欄位行的合併規則在閘道契約中固定,衝突鍵不會隨機取最後一個。
我還會檢查快取和簽名:使用者相關能力必須進入 Vary、私有快取或完全不快取,簽名兩端必須使用同一規範化。先影子解析,再灰度啟用,監控失敗率、誤開功能、欄位大小和解析耗時。舊客戶端繼續忽略該欄位,只有明確支援版本的客戶端才啟用新能力;這樣新增欄位不會把解析差異變成線上事故。
常見錯誤
- 把 JSON 放進一個字串標頭 → 代理和 SDK 仍需自訂解析,轉義與重複欄位語意不一致 → 使用 RFC 定義的 Item、List 或 Dictionary,並寫出成員語意。
- 用
split(',')解析 → 引號、內層清單和參數中的分隔符會被錯誤切開 → 使用規範解析器並加入非法語法測試。 - 未知參數直接報錯 → 新傳送方無法與舊客戶端共存 → 對可擴充參數採用忽略策略,只有已知安全約束失敗才拒絕。
- 解析失敗預設開啟能力 → 中介軟體篡改或截斷欄位可能誤開實驗或權限 → 失敗回到安全預設值,並記錄分類指標。
- 忽略快取變化 → 個人化能力被 CDN 復用給其他使用者 → 按欄位變化設定
Vary、私有快取或完全不快取。
追問及應對
追問一:為什麼不用回應 JSON?
如果能力只在回應體中使用,JSON 可以更適合業務資料。選擇 Structured Fields 的理由是它屬於 HTTP 元資料,閘道、快取策略和通用客戶端可以在讀取正文前處理。兩者不能混用語意;同一能力若同時出現,必須規定哪個是權威並檢測不一致。
追問二:多個代理把欄位合併了怎麼辦?
先在契約中規定欄位是否允許列表式重複,並在入口把多行規範化成單一解析輸入。對 Dictionary 的重複鍵採用拒絕或明確衝突規則,不依賴「最後一個獲勝」。整合測試覆蓋 HTTP/1.1 多行、HTTP/2 欄位表示和 CDN 實際行為。
追問三:新參數需要非 ASCII 文字怎麼辦?
不要把 UTF-8 直接塞進只支援 ASCII 的 String。可以定義 RFC 允許的 Display String 擴充,或把展示文字留在回應體並讓結構化欄位只攜帶穩定 ID。先確認所有中介軟體和 SDK 都支援該型別,再透過版本能力協商啟用。
追問四:解析器出現高 CPU 峰值怎麼處理?
立即收緊位元組、成員、巢狀和參數數量上限,並對超限欄位按缺失處理。保留原始輸入雜湊和失敗類別用於定位,不記錄完整內容。將解析移到受限執行緒或程序只能緩解資源問題,不能取代語法上限和灰度回退。