Skip to content
ADP
API Design PrincipleBETA

[ADP-403] 問題類型設計

TIP

2025.05.09 根據 RFC 9457,type 欄位必須足夠通用,但實務上有時需更細緻的子類型以利客戶端處理。經多方實作經驗,本規範明確建議:不應再設計 extraType 欄位,所有問題分類都應統一以 type URI 表示,避免多餘欄位造成混淆。

核心原則

  1. 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 可以被解析到。

  1. 相對 URI:當不確定問題發布位置或是否發布時,可(MAY)使用相對 URI reference。

  2. 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說明
10001404/problems/unknown-account找不到帳號
10003404/problems/unknown-channel找不到頻道
10013404/problems/unknown-user找不到使用者
20001403/problems/only-bots-allowed僅允許機器人操作
20009403/problems/explicit-content禁止明確內容
30001429/problems/rate-limited請求過於頻繁(限流)
50001403/problems/missing-access權限不足
50013403/problems/missing-permissions缺少必要權限
50035400/problems/invalid-form-body請求內容不合法
50034400/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 明確指出可以設計特定於某問題類型的欄位,因此倘若想要針對同一問題類型設計多種子類型,可以

  1. 考慮使用 / 或者 - 來區分
  2. 使用特定於問題類型的擴充欄位
  1. 應該(SHOULD)盡可能地使用通用或已存在的問題類型。

  2. 如果確實需要其他資訊,可以針對問題類型(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 錯誤。

  1. 檢視現有的 HTTP 狀態碼,參考 ADP-200
  2. 如果語意符合,使用該 HTTP 狀態碼的 URI reference 轉換(如:/problems/not-found)。

TIP

根據 RFC9457 原始的定義,about:blank 作為問題類型是被推薦的,但是本規範設計者認為如果要以 type 作為問題類型索引,應該盡可能直接指出而不是先看到 about:blank 再去檢查 status 代表的語意。

  1. 如果語意不符合,應查閱預定義的問題類型列表。
  2. 如果沒有匹配的狀態,應提出新類型,確保它們足夠通用。
  3. 使用 detail 欄位提供修正錯誤的方法。
  4. 使用 errors 欄位描述多個錯誤。對於驗證錯誤,則使用 validationErrors。詳見 ADP-404

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: 重新設計本條目內容