Skip to content
ADP
API Design PrincipleBETA

[ADP-401] HTTP 問題基礎

概述

錯誤回應是任何 API 的重要組成部分。顯然,HTTP 狀態碼對於錯誤(4xx,5xx)無法為 API 調用者提供足夠的資訊來準確理解發生了什麼。

過去我們可能會引入自定義錯誤代碼來描述標準 HTTP 狀態錯誤代碼以外的錯誤,但這並不清晰也不直觀。

目前 RFC 9457 提供了一種描述錯誤的標準方式,本 ADP 基於 RFC 9457 提供了一種設計錯誤回應的標準方法。

指導原則

  • 錯誤回應必須 (MUST) 與 HTTP Problem 規範相容。
  • 錯誤回應必須 (MUST) 採用 JSON 格式。
  • 錯誤回應的 Content-Type 標頭必須 (MUST) 為 application/problem+json
  • 問題類型設計請參見 ADP-403

Q & A

  • 可以針對所有 HTTP Problem 統一添加新欄位嗎?

    • 理論上在 RFC 9457 中明確指出擴充欄位要針對問題類型。
    • 如果有此需求(非特定問題類型而是對 HTTP Problem 做擴充),看是否需要通過添加 Header 來處理(metadata),或者提出設計變更到本 ADP。
  • 客戶端對於 HTTP Problem 如何應對?

    • 客戶端應該(SHOULD)根據 type 來處理問題。
    • 客戶端應該(SHOULD)忽略它目前未知或與定義中格式不同的欄位。
  • 哪些欄位是必要的?

    • RFC 9457 並未明確定義哪些欄位為可選,參考 Zalando 的 Problem 後決定:
      • 如有需要,可以 (MAY) 省略 detail,甚至 title 欄位,前提是 typestatus 已足以說明問題。
      • type 為必要且最重要欄位,詳細設計請參考 ADP-403
      • status 亦為必要欄位,主要作為 HTTP Status Header 的轉譯,方便錯誤物件轉發處理。
      • title 用於補充說明 type,若 type 已明確可省略。若支援內容協商,title 應 (SHOULD) 支援本地化。
      • detail 建議 (SHOULD) 提供用戶修復問題的具體指示,應以口語化語言撰寫,並支援多語言(如有 Accept-Language)。
        • detail 的重要性次於 title,若存在應聚焦於解決方法。
      • instance 在 Zandando Problem Schema 中為可選,參考後也定為可選,但建議採用程式化方式帶入此欄位(不需要手動在每個錯誤回應中填寫)。
  • 多語言支援?

    • 若 API 支援 Accept-Language,detail 應 (SHOULD) 提供多語言內容。無論是否支援多語言,只要有 detail 欄位,應 (SHOULD) 指定 Content-Language

錯誤回應結構

錯誤回應的範例:

http
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{
    type: '/problems/predefined-type',
    status: 500,
    title: '簡短的人類可讀錯誤描述',
    instance: '/items/12345',
    detail: '指引使用者如何修復問題'
}

錯誤回應的基本結構應如下:

屬性描述必需示例
type(string, uri-reference)必須是一個 URI(提示1)/problems/{problem-type}
title(string)問題類型的簡短、人類可讀摘要"Validation Error"
status(integer)原始伺服器為此問題實例生成的 HTTP 狀態碼400
detail(string)特定於此問題實例的人類可讀解釋(提示2)"提供的輸入無效,請提供正確格式。"
instance(string, uri-reference)標識問題特定實例的 URI 引用"/shop-items/123456"

提示1

理想情況下,這應該指向一個真實的網站,以提供更多關於該類型的資訊,正如 RFC 建議的那樣。

如果我們在引入問題類型時無法提供絕對 URI,使用相對 URI 是可以接受的。如:/problems/invalid-input

提示2 關於 ProblemJSON.detail

  • 如果其他屬性中已有足夠的資訊,detail 可以(MAY)為空。
  • detail 應為用戶提供如何修復問題的指示。也就是說,它應該(SHOULD)使用口語化的英語(或如果您的 API 支持 Accept-Language,則使用其他語言),如 RFC9457 中定義的那樣。
  • detailtitle 的區別: title 是一個更通用的問題簡短描述,而 detail 提供了修復問題的具體指示。
  • 如果您的 API 支持 Accept-Language,detail 可能是多語言的。無論您是否支持多語言,只要有 detail 屬性(property),就應該(SHOULD)指定 Content-Language

設計思路

許多網站使用自定義錯誤系統來描述 HTTP 狀態碼以外的自定義錯誤,例如額外的基於數字的代碼。然而,用戶仍然需要從互聯網查詢代碼的含義,因為不同應用程序之間沒有標準。

示例

讓我們以 Windows 錯誤代碼為例。參見 https://learn.microsoft.com/zh-tw/windows-hardware/drivers/debugger/bug-check-code-reference2

應該這樣做

json
{
  "type": "/problems/ipt-watchdog-timeout",
  "status": 500,
  "detail": "...."
}

不應該這樣做

json
{
  "status": "0x0001db"
}

401 Unauthorized 範例

以下為 401 Unauthorized(未授權)錯誤的 HTTP Problem 標準範例:

基本範例

json
{
  "type": "/problems/unauthorized",
  "title": "未授權的存取",
  "status": 401,
  "detail": "您必須先登入,才能存取此資源。"
}

帶有本地化與 instance 的範例

json
{
  "type": "https://example.com/problems/unauthorized",
  "title": "未授權的存取",
  "status": 401,
  "detail": "請登入您的帳號後重試。",
  "instance": "/orders/123456"
}

進階範例

json
{
  "type": "https://example.com/problems/unauthorized",
  "title": "未授權的存取",
  "status": 401,
  "detail": "Access token 已過期,請重新登入取得新的授權。",
  "instance": "/api/v1/profile"
}

參考

標準參考

設計參考

更新紀錄

  • 2025.05.09: 移除 extraType 設計,補充 Q&A 與範例區塊,潤飾用語。