知識庫 / REST RSS 訂閱

REST API 錯誤處理最佳實踐

Architecture,REST
HongKong
5
03:47 AM · Dec 06 ,2025

1. 概述

REST 是一種無狀態架構,客户端可以訪問和操作服務器上的資源。通常,RESTful 服務使用 HTTP 來宣佈它們管理的一組資源,並提供一個 API,允許客户端獲取或修改這些資源的當前狀態。

在本教程中,我們將學習處理 REST API 錯誤的一些最佳實踐,包括為用户提供相關信息的有用方法,來自大型網站的示例以及使用示例 Spring REST 應用程序的實際實現。

2. HTTP 狀態碼

當客户端向 HTTP 服務器發送請求,且服務器成功接收到該請求時,服務器必須通知客户端請求是否成功處理或未處理。

HTTP 通過五類狀態碼來實現這一點:

  • 100-級別(指示性) – 服務器確認請求
  • 200-級別(成功) – 服務器已按照預期完成請求
  • 300-級別(重定向) – 客户端需要執行進一步操作以完成請求
  • 400-級別(客户端錯誤) – 客户端發送了無效請求
  • 500-級別(服務器錯誤) – 由於服務器端錯誤,未能滿足有效的請求

根據響應代碼,客户端可以推斷出特定請求的結果。

3. 處理錯誤

提供適當的狀態碼是處理錯誤的最初步驟。此外,我們可能還需要在響應體中提供更多信息。

3.1. 基本響應

處理錯誤的最簡單方法是使用適當的狀態碼進行響應。

以下是一些常見的響應碼:

  • 400 Bad Request – 客户端發送了無效請求,例如缺少必需的請求主體或參數。
  • 401 Unauthorized – 客户端未成功與服務器進行身份驗證。
  • 403 Forbidden – 客户端已進行身份驗證,但沒有權限訪問請求的資源。
  • 404 Not Found – 請求的資源不存在。
  • 412 Precondition Failed – 請求頭字段中的一個或多個條件評估結果為 false。
  • 500 Internal Server Error – 服務器在處理請求時發生了通用錯誤。
  • 503 Service Unavailable – 請求的服務不可用。

雖然這些代碼是基礎的,但它們允許客户端了解發生的錯誤的大致性質。例如,如果收到 403 錯誤,我們知道我們缺乏訪問請求資源的權限。然而,在許多情況下,我們需要在響應中提供補充信息。

500 錯誤表明在處理請求時,服務器上發生了某些問題或異常。通常,此內部錯誤不屬於我們的客户端業務。

因此,為了儘量減少這些響應對客户端的影響,我們應該盡力處理或捕獲內部錯誤,並在可能的情況下使用其他適當的狀態碼。

例如,如果由於請求的資源不存在而發生異常,我們應該將其暴露為 404 錯誤,而不是 500 錯誤。

這並不意味着 500 永遠不應該返回,只是它應該用於意外條件——例如服務中斷——導致服務器無法執行請求時。

3.2. 默認 Spring 錯誤響應

這些原則過於普遍,Spring 已經將其編碼到默認的錯誤處理機制中。

為了説明,假設我們有一個 簡單的 Spring REST 應用,該應用管理書籍,並提供一個端點來根據 ID 檢索書籍:

curl -X GET -H "Accept: application/json" http://localhost:8082/spring-rest/api/book/1

如果不存在 ID 為 1 的書籍,我們期望我們的控制器會拋出 BookNotFoundException

對該端點執行 GET 請求,我們看到該異常被拋出,並且這是響應體:

{
    "timestamp":"2019-09-16T22:14:45.624+0000",
    "status":500,
    "error":"Internal Server Error",
    "message":"No message available",
    "path":"/api/book/1"
}

請注意,此默認錯誤處理程序包含錯誤發生的時間戳、HTTP 狀態碼、標題(error 字段)、如果啓用了默認錯誤中的消息則消息(默認情況下為空)以及錯誤發生的 URL 路徑。

這些字段為客户端或開發人員提供信息,以幫助他們解決問題,並且也構成標準錯誤處理機制中的幾個字段。

請注意,Spring 在我們的 BookNotFoundException 被拋出時,會自動返回 HTTP 狀態碼 500。雖然某些 API 會返回 500 狀態碼或其他通用狀態碼,正如我們將在 Facebook 和 Twitter API 中看到的那樣,為了簡化起見,儘可能使用最具體的錯誤代碼

在我們的示例中,我們可以添加一個 @ControllerAdvice,以便當 BookNotFoundException 被拋出時,我們的 API 返回 404 狀態碼,以表示 未找到,而不是 500 內部服務器錯誤

3.3. 更詳細的響應

如上所示的 Spring 示例中,有時狀態碼本身不足以展示錯誤的具體信息。當需要時,我們可以使用響應體向客户端提供額外信息。

在提供詳細響應時,我們應該包含:

  • 錯誤 – 唯一的錯誤標識符
  • 消息 – 簡短的人類可讀的消息
  • 詳細信息 – 錯誤的更詳細的解釋

例如,如果客户端發送帶有錯誤的憑據的請求,我們可以發送以下 401 響應:

{
    "error": "auth-0001",
    "message": "Incorrect username and password",
    "detail": "Ensure that the username and password included in the request are correct"
}

錯誤字段不應與響應代碼匹配。相反,它應是特定於我們應用程序的唯一錯誤代碼。通常,錯誤字段沒有約定,除了它必須是唯一的。

通常,此字段僅包含字母數字字符和連接字符,例如短劃線或下劃線。例如,0001auth-0001incorrect-user-pass是唯一錯誤代碼的典範示例。

通常,請求體中的消息部分被認為是用户界面上可呈現的。因此,如果支持國際化,我們應將此標題翻譯。因此,如果客户端發送帶有與法語相對應的Accept-Language標頭請求,則標題值應翻譯為法語。

請求體中的詳細信息部分是供客户端開發人員使用的,而不是供最終用户使用的,因此不需要翻譯。

此外,我們還可以提供一個 URL——例如幫助字段——供客户端遵循以獲取更多信息:

{
    "error": "auth-0001",
    "message": "Incorrect username and password",
    "detail": "Ensure that the username and password included in the request are correct",
    "help": "https://example.com/help/error/auth-0001"
}

有時,我們可能需要為單個請求報告多個錯誤。

在這種情況下,我們應該以列表的形式返回這些錯誤:

{
    "errors": [
        {
            "error": "auth-0001",
            "message": "Incorrect username and password",
            "detail": "Ensure that the username and password included in the request are correct",
            "help": "https://example.com/help/error/auth-0001"
        },
        ...
    ]
}

當發生單個錯誤時,我們會返回一個包含一個元素的列表。

請注意,響應多個錯誤可能會過於複雜,對於簡單的應用程序來説。在許多情況下,僅返回第一個或最重要的錯誤就足夠了。

3.4. 標準化響應體

雖然大多數 REST API 遵循相似的約定,但具體細節通常存在差異,包括字段名稱和響應體中包含的信息。這些差異使得庫和框架難以統一處理錯誤。

為了標準化 REST API 的錯誤處理,IETF 設計了 RFC 7807,它定義了一個通用的錯誤處理模式。

該模式由五部分組成:

  1. type – 一個 URI 標識符,用於分類錯誤
  2. title – 關於錯誤的簡短、可讀的描述
  3. status – HTTP 響應代碼(可選)
  4. detail – 錯誤的可讀性解釋
  5. instance – 一個 URI,用於標識錯誤的特定發生

與其使用我們自定義的響應體,我們可以將我們的響應體轉換為:

{
    "type": "/errors/incorrect-user-pass",
    "title": "Incorrect username or password.",
    "status": 401,
    "detail": "Authentication failed due to incorrect username or password.",
    "instance": "/login/log/abc123"
}

請注意,字段用於分類錯誤的類型,而 字段則標識錯誤在類似類和對象中發生的具體實例。

通過使用 URI,客户端可以遵循這些路徑以獲取有關錯誤的更多信息,類似於 HATEOAS 鏈接可以用於導航 REST API。

遵守 RFC 7807 是可選的,但如果需要保持一致性,則具有優勢。

4. 示例

上述實踐在許多流行的 REST API 中都有廣泛應用。雖然各個網站字段或格式的具體名稱可能存在差異,但總體模式幾乎是統一的。

4.1. 推特

以下代碼演示了在不提供所需身份驗證數據的情況下發送一個 GET 請求:

curl -X GET https://api.twitter.com/1.1/statuses/update.json?include_entities=true

Twitter API 返回以下錯誤信息:

{
    "errors": [
        {
            "code":215,
            "message":"Bad Authentication data."
        }
    ]
}

此響應包含一個包含單個錯誤的列表,其中包含錯誤代碼和消息。在Twitter的情況下,不存在詳細的消息,而是使用一個通用的錯誤——而不是更具體的401錯誤——來表示身份驗證失敗。

有時,使用更通用的狀態碼會更方便,如我們在下面的Spring示例中看到的。這允許開發人員捕獲異常組,而無需區分應返回的狀態碼。但是,儘可能使用最具體的狀態碼。

4.2. Facebook

類似於 Twitter,Facebook 的 Graph REST API 也包含詳細信息在響應中。

讓我們執行一個 POST 請求來與 Facebook Graph API 進行身份驗證:

curl -X GET https://graph.facebook.com/oauth/access_token?client_id=foo&client_secret=bar&grant_type=baz

我們收到以下錯誤:

{
    "error": {
        "message": "Missing redirect_uri parameter.",
        "type": "OAuthException",
        "code": 191,
        "fbtrace_id": "AWswcVwbcqfgrSgjG80MtqJ"
    }
}

類似於 Twitter,Facebook 也使用通用的錯誤碼——而不是更具體的 400 級別錯誤——來表示失敗。除了包含消息和數字代碼外,Facebook 還包含一個 字段,用於對錯誤進行分類,以及一個跟蹤 ID (fbtrace_id),它作為內部支持標識符。

5. 結論

本文探討了 REST API 錯誤處理的最佳實踐:

  • 提供具體的 HTTP 狀態碼
  • 在響應體中包含額外信息
  • 以統一的方式處理異常

雖然錯誤處理的具體細節會因應用程序而異,但這些通用原則幾乎適用於所有 REST API,並在可能的情況下應予以遵循。

這樣做不僅允許客户端以一致的方式處理錯誤,還簡化了我們在實現 REST API 時編寫的代碼。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.