2025年6月10日 星期二

RESTful API 的設計原則與最佳實務。

 好的,RESTful API (Representational State Transfer) 是一種設計網路服務的架構風格,而不是一套嚴格的標準。它的核心是將網路上的事物抽象為「資源 (Resources)」,並透過標準的 HTTP 方法對這些資源進行操作。遵循 RESTful 原則能讓 API 更具可讀性、可擴展性、易於理解和使用。

以下是 RESTful API 的設計原則與最佳實務:


RESTful API 的核心設計原則

RESTful API 的設計基於以下六大原則:

  1. 客戶端-伺服器分離 (Client-Server Separation):

    • 原則: 客戶端和伺服器是獨立的實體。客戶端負責使用者介面和使用者狀態,伺服器負責資料儲存和處理。兩者之間只透過網路連接進行溝通。
    • 目的: 提高可擴展性,允許多個客戶端使用同一個 API,並獨立演進。
  2. 無狀態性 (Statelessness):

    • 原則: 伺服器不會在兩次請求之間儲存任何客戶端的上下文資訊 (Session State)。每次客戶端的請求都必須包含所有必要的資訊,以便伺服器能夠理解和處理該請求。
    • 目的: 提高可擴展性、可靠性和簡單性。任何請求都可以在任何可用的伺服器上處理,有利於負載均衡和容錯。
  3. 可快取性 (Cacheability):

    • 原則: 伺服器回應應明確指出其資源是否可快取。客戶端或中間代理可以根據這些指示快取資源,以減少重複請求,提高效能。
    • 目的: 提升 API 效能,減少網路流量,降低伺服器負載。
  4. 分層系統 (Layered System):

    • 原則: 客戶端和伺服器之間的通訊可以通過多個中間層 (如代理伺服器、負載均衡器、快取等)。客戶端無需知道它是在直接與伺服器通訊還是在與中間層通訊。
    • 目的: 提高系統的可擴展性、安全性、靈活性和彈性。
  5. 統一介面 (Uniform Interface):

    • 原則: 這是 RESTful 最核心且最重要的原則。它規定了客戶端和伺服器之間的互動方式必須保持一致和標準化。主要包含以下幾點:
      • 資源的識別: 使用 URI (Uniform Resource Identifier) 唯一標識資源。
      • 資源的表現形式: 資源以某種標準格式 (如 JSON, XML) 傳輸。
      • 自我描述性訊息: 每個請求都包含足夠的資訊,讓伺服器可以理解請求的意圖。回應也包含足夠的資訊,讓客戶端可以理解回應的內容。
      • HATEOAS (Hypermedia As The Engine Of Application State): 這是統一介面中最難實現的部分。伺服器的回應應該包含與資源相關的其他資源的連結,以便客戶端可以在不依賴預設 URI 結構的情況下導航和發現資源。
  6. 程式碼隨需 (Code-On-Demand) (可選):

    • 原則: 伺服器可以臨時擴展或自定義客戶端的功能,通過下載並執行程式碼 (如 JavaScript)。這是唯一一個可選原則。
    • 目的: 提高客戶端功能擴展性,但實際應用中較少見,或多用於特殊場景(如動態表單驗證)。

RESTful API 的最佳實務 (Best Practices)

1. 資源命名 (Resource Naming)

  • 使用名詞表示資源,避免使用動詞。 資源應該是具體的、有意義的。
    • 好: /users, /products, /orders/123/items
    • 不好: /getAllUsers, /createProduct, /deleteOrder
  • 使用複數名詞。 表示資源的集合。
    • 好: /users, /products, /orders
    • 不好: /user, /product (除非確實只表示單一實例的資源類型)
  • 使用巢狀結構表示關聯資源。
    • 好: /users/{user_id}/orders, /orders/{order_id}/items
    • 不好: /user_orders/{user_id}, /order_items_of_order/{order_id}
  • 保持 URI 簡潔、可讀和層次分明。
    • 好: /customers/123/orders
    • 不好: /get_customer_id_123_orders
  • 避免在 URI 中使用檔案副檔名。 使用 Accept Header 來協商內容類型。
    • 好: /users/1
    • 不好: /users/1.json, /users/1.xml

2. HTTP 方法 (HTTP Verbs)

利用標準的 HTTP 方法來表示對資源的操作:

  • GET (讀取): 從伺服器檢索資源。安全且冪等。
    • GET /users:獲取所有使用者列表。
    • GET /users/123:獲取 ID 為 123 的使用者。
  • POST (新增): 在伺服器上創建新資源。通常不冪等。
    • POST /users:創建一個新使用者。請求體包含使用者資料。
  • PUT (完整更新/替換): 完整更新或替換指定資源。如果資源不存在,通常會創建它。冪等。
    • PUT /users/123:用請求體中的資料完全替換 ID 為 123 的使用者資料。
  • PATCH (部分更新): 對指定資源進行部分更新。通常不冪等。
    • PATCH /users/123:只更新 ID 為 123 的使用者的一些屬性。
  • DELETE (刪除): 從伺服器刪除指定資源。冪等。
    • DELETE /users/123:刪除 ID 為 123 的使用者。

3. HTTP 狀態碼 (HTTP Status Codes)

使用標準的 HTTP 狀態碼來指示 API 請求的結果,幫助客戶端理解回應的含義:

  • 2xx 成功 (Success):
    • 200 OK: 請求成功,並返回回應主體。
    • 201 Created: 成功創建了新資源 (通常是 POST 請求)。回應主體應包含新資源的 URI。
    • 204 No Content: 請求成功,但沒有內容需要返回 (例如 DELETE 請求成功)。
  • 4xx 客戶端錯誤 (Client Error):
    • 400 Bad Request: 請求格式錯誤或參數無效。
    • 401 Unauthorized: 請求需要認證。
    • 403 Forbidden: 客戶端沒有權限存取資源。
    • 404 Not Found: 請求的資源不存在。
    • 405 Method Not Allowed: 請求方法不被允許 (例如對只讀資源發送 POST)。
    • 409 Conflict: 請求因與目標資源的當前狀態衝突而無法完成 (例如在同一時間修改同一資源)。
    • 429 Too Many Requests: 客戶端在給定時間內發送了太多請求 (速率限制)。
  • 5xx 伺服器錯誤 (Server Error):
    • 500 Internal Server Error: 伺服器在處理請求時發生未知錯誤。
    • 502 Bad Gateway: 作為閘道或代理的伺服器從上游伺服器收到無效回應。
    • 503 Service Unavailable: 伺服器暫時無法處理請求 (可能因過載或維護)。

4. 資料格式 (Data Formats)

  • 使用 JSON (JavaScript Object Notation) 為主要資料交換格式。 它輕量、易讀、易於解析,並且幾乎所有程式語言都支援。
  • 設置正確的 Content-Type 和 Accept Header。
    • 請求: Content-Type: application/json (指明請求體格式)
    • 回應: Accept: application/json (客戶端希望接受的格式)
    • 伺服器回應: Content-Type: application/json (指明回應體格式)

5. 版本控制 (Versioning)

當 API 發生不相容的變更時,需要引入版本控制以避免破壞現有客戶端。

  • URI 版本控制 (URI Versioning) - 最常用且推薦:
    • https://api.example.com/v1/users
    • https://api.example.com/v2/users
  • Header 版本控制 (Header Versioning):
    • Accept: application/vnd.example.v1+json
  • 缺點: 避免在查詢參數中做版本控制 (/users?version=2),因為這會使得資源的 URI 不唯一。

6. 錯誤處理 (Error Handling)

  • 返回有意義的錯誤訊息。 錯誤訊息應包含:
    • HTTP 狀態碼。
    • 一個簡潔的錯誤代碼 (Error Code)。
    • 一個人類可讀的錯誤訊息 (Human-readable message)。
    • 可能的話,提供更多錯誤詳情或指向文件連結。
  • 範例錯誤回應:
    JSON
    {
      "status": 400,
      "code": "INVALID_INPUT",
      "message": "請求參數無效",
      "errors": [
        {
          "field": "email",
          "message": "無效的電子郵件格式"
        },
        {
          "field": "password",
          "message": "密碼長度必須至少為 8 個字元"
        }
      ]
    }
    

7. 認證與授權 (Authentication & Authorization)

  • 無狀態認證: 由於 RESTful 是無狀態的,通常使用基於 Token 的認證方式。
    • JWT (JSON Web Tokens): 常用於無狀態認證,客戶端在每次請求的 Authorization Header 中攜帶 Token。
    • OAuth 2.0: 用於授權,允許第三方應用程式安全地存取使用者資料,而無需分享使用者憑證。
  • 使用 HTTPS: 所有 API 通訊都應透過 HTTPS 進行加密,以保護數據安全和隱私。

8. 過濾、分頁與排序 (Filtering, Pagination & Sorting)

對於返回集合資源的請求,客戶端通常需要控制返回的數據量和順序。

  • 過濾 (Filtering): 使用查詢參數來過濾集合。
    • GET /products?category=electronics&price_lt=500
  • 分頁 (Pagination): 使用查詢參數來控制返回數據的頁碼和每頁數量。
    • GET /users?page=2&limit=20
    • 建議: 除了 pagelimit,回應中應包含總頁數、下一頁/上一頁的連結等元數據。
  • 排序 (Sorting): 使用查詢參數來指定排序字段和排序方向。
    • GET /products?sort=price,desc (按價格降序)
    • GET /users?sort=name,asc&sort=created_at,desc (多個排序條件)

9. HATEOAS (Hypermedia As The Engine Of Application State) (高階實踐)

這是 RESTful 最難實現但也是最「RESTful」的原則。它意味著 API 的回應應該包含相關的連結,讓客戶端無需硬編碼 URI 即可發現和導航相關資源。

  • 範例:
    JSON
    {
      "id": 123,
      "name": "John Doe",
      "email": "john.doe@example.com",
      "_links": [
        {
          "rel": "self",
          "href": "/users/123",
          "method": "GET"
        },
        {
          "rel": "orders",
          "href": "/users/123/orders",
          "method": "GET",
          "title": "查看所有訂單"
        },
        {
          "rel": "update",
          "href": "/users/123",
          "method": "PUT",
          "title": "更新使用者資訊"
        }
      ]
    }
    
  • 好處: 提高 API 的可發現性和彈性,客戶端可以從單一入口點探索整個 API。
  • 挑戰: 實現複雜,許多實際的 RESTful API 常常會省略這一點,尤其是在內部服務中。

總結

RESTful API 的設計旨在提供一個簡單、可擴展、高效能且易於理解的網路服務介面。雖然它的核心原則是通用的,但最佳實踐則是在實際開發中不斷演進和完善的。遵循這些原則和實踐,可以幫助你構建出高質量、易於維護和使用的 API。

沒有留言:

張貼留言

網誌存檔