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 欄位描述多個錯誤。

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