好的,RESTful API (Representational State Transfer) 是一種設計網路服務的架構風格,而不是一套嚴格的標準。它的核心是將網路上的事物抽象為「資源 (Resources)」,並透過標準的 HTTP 方法對這些資源進行操作。遵循 RESTful 原則能讓 API 更具可讀性、可擴展性、易於理解和使用。
以下是 RESTful API 的設計原則與最佳實務:
RESTful API 的核心設計原則
RESTful API 的設計基於以下六大原則:
-
客戶端-伺服器分離 (Client-Server Separation):
- 原則: 客戶端和伺服器是獨立的實體。客戶端負責使用者介面和使用者狀態,伺服器負責資料儲存和處理。兩者之間只透過網路連接進行溝通。
- 目的: 提高可擴展性,允許多個客戶端使用同一個 API,並獨立演進。
-
無狀態性 (Statelessness):
- 原則: 伺服器不會在兩次請求之間儲存任何客戶端的上下文資訊 (Session State)。每次客戶端的請求都必須包含所有必要的資訊,以便伺服器能夠理解和處理該請求。
- 目的: 提高可擴展性、可靠性和簡單性。任何請求都可以在任何可用的伺服器上處理,有利於負載均衡和容錯。
-
可快取性 (Cacheability):
- 原則: 伺服器回應應明確指出其資源是否可快取。客戶端或中間代理可以根據這些指示快取資源,以減少重複請求,提高效能。
- 目的: 提升 API 效能,減少網路流量,降低伺服器負載。
-
分層系統 (Layered System):
- 原則: 客戶端和伺服器之間的通訊可以通過多個中間層 (如代理伺服器、負載均衡器、快取等)。客戶端無需知道它是在直接與伺服器通訊還是在與中間層通訊。
- 目的: 提高系統的可擴展性、安全性、靈活性和彈性。
-
統一介面 (Uniform Interface):
- 原則: 這是 RESTful 最核心且最重要的原則。它規定了客戶端和伺服器之間的互動方式必須保持一致和標準化。主要包含以下幾點:
- 資源的識別: 使用 URI (Uniform Resource Identifier) 唯一標識資源。
- 資源的表現形式: 資源以某種標準格式 (如 JSON, XML) 傳輸。
- 自我描述性訊息: 每個請求都包含足夠的資訊,讓伺服器可以理解請求的意圖。回應也包含足夠的資訊,讓客戶端可以理解回應的內容。
- HATEOAS (Hypermedia As The Engine Of Application State): 這是統一介面中最難實現的部分。伺服器的回應應該包含與資源相關的其他資源的連結,以便客戶端可以在不依賴預設 URI 結構的情況下導航和發現資源。
- 原則: 這是 RESTful 最核心且最重要的原則。它規定了客戶端和伺服器之間的互動方式必須保持一致和標準化。主要包含以下幾點:
-
程式碼隨需 (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: 用於授權,允許第三方應用程式安全地存取使用者資料,而無需分享使用者憑證。
- JWT (JSON Web Tokens): 常用於無狀態認證,客戶端在每次請求的
- 使用 HTTPS: 所有 API 通訊都應透過 HTTPS 進行加密,以保護數據安全和隱私。
8. 過濾、分頁與排序 (Filtering, Pagination & Sorting)
對於返回集合資源的請求,客戶端通常需要控制返回的數據量和順序。
- 過濾 (Filtering): 使用查詢參數來過濾集合。
GET /products?category=electronics&price_lt=500
- 分頁 (Pagination): 使用查詢參數來控制返回數據的頁碼和每頁數量。
GET /users?page=2&limit=20
- 建議: 除了
page
和limit
,回應中應包含總頁數、下一頁/上一頁的連結等元數據。
- 排序 (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。
沒有留言:
張貼留言