[ADP-403] 問題類型設計
TIP
2025.05.09 根據 RFC 9457,type 欄位必須足夠通用,但實務上有時需更細緻的子類型以利客戶端處理。經多方實作經驗,本規範明確建議:不應再設計 extraType
欄位,所有問題分類都應統一以 type
URI 表示,避免多餘欄位造成混淆。
核心原則
- URI 參考:必須(MUST)是人類可讀的 URI reference。
TIP
在 RFC9457 中,僅有提到 type 必須是相對或絕對 uri。
但是,根據 RFC9457 中提到 title 欄位是在人類無法理解 type 欄位時用來補充說明這個前提下,以及各個 HTTP Problem 的實作,本規範設計者認為 type 必須被設計為 human readable 的 uri reference。
所以雖然 /1234088abc 根據 RFC9457 原始定義也是正確的 uri,但是在語意理解的角度下,我們認為應該避免使用這種 uri 作為問題類型。
也許當有一天應用程式成長到某個程度時,整個問題類型列表根據需求會長到一個無法維護的狀態,此時有可能會變成設計 type: https://api.example.com/problems/1234088abc
作為問題類型是可以接受的,但是必須(MUST)真實存在此 uri 可以被解析到。
相對 URI:當不確定問題發布位置或是否發布時,可(MAY)使用相對 URI reference。
API 提供者應該(SHOULD)提供一個問題類型列表。
常見 HTTP 狀態碼與對應 HTTP Problem type
以下表格整理常見 (API 會使用的) HTTP 狀態碼(基本上來自 ADP-200 所定義的 HTTP 狀態碼)及其建議對應的 Problem type URI,協助 API 設計者統一錯誤回應格式:
問題類型字串的設計參考自 Smartbear 的 問題詳情註冊表。
HTTP 狀態碼 | Problem Type URI | 說明 | 情境 |
---|---|---|---|
400 | /problems/bad-request | 請求格式錯誤 | |
400 | /problems/invalid-device-name | 裝置名稱格式錯誤 | |
401 | /problems/unauthorized | 未經授權 | |
403 | /problems/forbidden | 禁止存取 | |
404 | /problems/not-found | 找不到資源 | |
405 | /problems/method-not-allowed | 不允許的 HTTP 方法 | |
408 | /problems/request-timeout | 請求逾時 | |
409 | /problems/conflict | 資源衝突 | |
415 | /problems/unsupported-media-type | 不支援的媒體類型 | |
418 | /problems/teapot | 我是一個茶壺(彩蛋) | |
422 | /problems/unprocessable-entity | 無法處理的實體 | |
424 | /problems/failed-dependency | 依賴失敗 | |
425 | /problems/too-early | 請求過早 | |
426 | /problems/upgrade-required | 需要升級 | |
429 | /problems/too-many-requests | 請求過於頻繁 | Rate Limit 相關 |
500 | /problems/internal-server-error | 伺服器內部錯誤 | |
500 | /problems/server-sepcific-error-placeholder-1 | 伺服器自訂錯誤1 | |
500 | /problems/server-sepcific-error-placeholder-2 | 伺服器自訂錯誤2 | |
501 | /problems/not-implemented | 尚未實作 | |
502 | /problems/bad-gateway | 錯誤的閘道 | |
503 | /problems/service-unavailable | 服務暫時無法使用 | |
504 | /problems/gateway-timeout | 閘道逾時 | |
507 | /problems/insufficient-storage | 儲存空間不足 |
INFO
Q:我們可以擁有同一個 problem type 但是狀態碼不同嗎?
A:原則上是沒有不行,但是可能會造成辨識上的困擾,請謹慎使用。
400
以下節錄自 RFC9110
Discord API 錯誤碼對應 HTTP Problem
以下表格將 Discord API 常見錯誤碼轉換為 HTTP Problem 格式,便於設計參考:
Discord 錯誤碼 | HTTP 狀態碼 | Problem Type URI | 說明 |
---|---|---|---|
10001 | 404 | /problems/unknown-account | 找不到帳號 |
10003 | 404 | /problems/unknown-channel | 找不到頻道 |
10013 | 404 | /problems/unknown-user | 找不到使用者 |
20001 | 403 | /problems/only-bots-allowed | 僅允許機器人操作 |
20009 | 403 | /problems/explicit-content | 禁止明確內容 |
30001 | 429 | /problems/rate-limited | 請求過於頻繁(限流) |
50001 | 403 | /problems/missing-access | 權限不足 |
50013 | 403 | /problems/missing-permissions | 缺少必要權限 |
50035 | 400 | /problems/invalid-form-body | 請求內容不合法 |
50034 | 400 | /problems/invalid-asset | 資源格式不合法 |
TIP
目前針對問題類型的設計可能會有疑慮:RFC9457 並未提供足夠的範例用以描述問題類型該如何設計,比如 究竟是:not-found
搭配 /books
作為 instance 還是 book-not-found
還是 book/not-found
? 以及 /problems
是否應該作為問題類型的前綴。
本規範建議先在單一應用程式內選擇統一一種做法後,等待之後規範推廣更加深遠的情況下去做應用間的問題類型統合。
以下暫時提供一些範例:
/problems/not-found
/problems/user-not-found
/problems/out-of-organization
/problems/invalid-license
因為 RFC9457 明確指出可以設計特定於某問題類型的欄位,因此倘若想要針對同一問題類型設計多種子類型,可以
- 考慮使用
/
或者-
來區分 - 使用特定於問題類型的擴充欄位
應該(SHOULD)盡可能地使用通用或已存在的問題類型。
如果確實需要其他資訊,可以針對問題類型(MAY)添加額外屬性(property)來擴展問題 JSON
http{ "type": "https://example.dev/problems/out-of-credit", "title": "您的請求無效。", "credit": 10, "remainingCredit": 0, }
設計流程
INFO
在設計錯誤的時候,首先我們必須考慮到 UX SPEC 或 PM STORY/WIREFRAME 理論上不會定義出所有的錯誤, 換句話說,所有的 API 端點都應該存在一些預設的錯誤列表,並被記錄在 API 文件(OpenAPI)中。
本規範建議至少定義一個預設的 400 錯誤跟預設的 500 錯誤。
- 檢視現有的 HTTP 狀態碼,參考 ADP-200。
- 如果語意符合,使用該 HTTP 狀態碼的 URI reference 轉換(如:
/problems/not-found
)。
TIP
根據 RFC9457 原始的定義,about:blank
作為問題類型是被推薦的,但是本規範設計者認為如果要以 type 作為問題類型索引,應該盡可能直接指出而不是先看到 about:blank
再去檢查 status
代表的語意。
- 如果語意不符合,應查閱預定義的問題類型列表。
- 如果沒有匹配的狀態,應提出新類型,確保它們足夠通用。
- 使用
detail
欄位提供修正錯誤的方法。 - 使用
errors
欄位描述多個錯誤。
TIP
後端程式錯誤應該跟 API 錯誤分開考量。
API Server 內部可以定義任意的錯誤,但是最終都會需要透過某些機制轉換為特定 API 錯誤格式(以 RESTful API 為例,應該轉換為 HTTP Status Code 跟 HTTP Problem Details,至於 GraphQL 以及 gRPC 則會有對應的錯誤格式需要另外定義)。
想像中大部分的領域限定的後端內部程式錯誤應該被視為 500 錯誤,並在有需要的情況下透過 detail
欄位提供更詳細的錯誤資訊。
一般情況下,我們應該避免在 API 錯誤中加入過多的程式內部資訊,因為這些資訊對於客戶端來說是無意義的,反而會增加客戶端的維護成本。
另外,大部分的 API Error 可以被通用的 HTTP Status Code 所歸類。
本規範會列出一些通用的 HTTP Problem 該如何定義。
[已廢止] 子類型(extraType)設計提案
注意:本節為歷史設計,現已明確不建議使用 extraType 欄位,請統一以 type URI 表示所有問題分類。
早期考慮到有時需更細緻的子類型,曾提出 extraType
欄位設計。實務上,建議將子類型直接納入 type URI,例如:https://apidoc.alive.dev/problems/not-found/user-not-found
。
經過參考各種 HTTP Problem 的實作,我們認為應該避免引入
extraType
欄位,請以 type URI 層級式設計分類。json{ "type": "https://example.com/problems/authentication-error/user-not-found", "title": "Authentication Error", "status": 409, "detail": "Your session has expired due to JWT", }
參考資料
Changelog
2025.05.13
: 重新設計本條目內容