15個與語言無關的REST API設計技巧 - bluethl

banq發表於2022-03-11

在這篇文章中,我正在盡我最大的努力壓縮我所知道的關於什麼是好的 API 的一切。您的消費者會喜歡使用的 API。所有提示都與語言無關,因此它們適用於任何框架或技術。
 

1. 保持一致

  • 對欄位、資源和引數使用相同的大小寫(我更喜歡snake_case)
  • 使用複數或單數資源名稱(我更喜歡複數)
    • /users/{id},/orders/{id}或/user/{id},/order/{id}
  • 對所有端點使用相同的身份驗證和授權方法
  • 在 API 中使用相同的 HTTP 標頭
    • 例如Api-Key用於傳遞 API 金鑰
  • 根據響應型別使用相同的 HTTP 狀態程式碼
    • 例如404,當找不到資源時
  • 對相同型別的操作使用相同的 HTTP 方法
    • 例如DELETE刪除資源時

 

2. 使用ISO 8601 UTC 日期
在處理日期和時間時,API 應始終返回 ISO 8601 格式的字串。在特定時區顯示日期通常是客戶端應用程式關心的問題。

{
    "published_at": "2022-03-03T21:59:08Z"
}

 

3. 對公共端點進行例外處理
預設情況下,每個端點都應該需要授權。大多數端點都需要呼叫經過身份驗證的使用者,因此將其設為預設值是有意義的。如果需要公開呼叫端點,請顯式設定此端點以允許未經授權的請求。
 

4.提供健康檢查端點
GET /health提供一個確定服務是否健康的端點(例如)。其他應用程式(例如負載均衡器)可以呼叫此端點以在服務中斷時採取行動。
 

5. 版本化 API
確保對 API 進行版本控制並在每個請求中傳遞版本,這樣消費者就不會受到對另一個版本的任何更改的影響。API 版本可以使用 HTTP 標頭或查詢/路徑引數傳遞。即使是 API 的第一個版本(1.0)也應該明確地進行版本控制。
一些例子:

  • https://api.averagecompany.com/v1/health
  • https://api.averagecompany.com/health?api_version=1.0

 

6.接受API金鑰認證
如果 API 需要由第三方呼叫,則允許透過 API 金鑰進行身份驗證是有意義的。API 金鑰應使用自定義 HTTP 標頭(例如Api-Key)傳遞。它們應該有一個到期日期,並且必須可以撤銷活動金鑰,以便在它們受到損害時可以使它們失效。避免將 API 金鑰簽入原始碼控制(改用環境變數)。
 

7. 使用合理的 HTTP 狀態碼
使用傳統的 HTTP 狀態程式碼來指示請求的成功或失敗。不要使用太多,並在整個 API 中為相同的結果使用相同的狀態程式碼。一些例子:

  • 200取得普遍成功
  • 201為成功創作
  • 400對於來自客戶端的錯誤請求
  • 401對於未經授權的請求
  • 403缺少許可權
  • 404對於缺少的資源
  • 429對於太多的請求
  • 5xx對於內部錯誤(應不惜一切代價避免這些錯誤)

  

8.使用合理的HTTP方法
HTTP 方法有很多,但最重要的是:

  • POST用於建立資源
    • POST /users
  • GET用於閱讀資源(單個資源和集合)
    • GET /users
    • GET /users/{id}
  • PATCH用於對資源應用部分更新
    • PATCH /users/{id}
  • PUT用於對資源應用完整更新(替換當前資源)
    • PUT /users/{id}
  • DELETE用於刪除資源
    • DELETE /users/{id}

  

9.使用不言自明、簡單的名字
大多數端點都是面向資源的,應該這樣命名。不要新增可以從其他地方推斷出的不必要的資訊。這也適用於欄位名稱。
好:

  • GET /users=> 檢索使用者
  • DELETE /users/{id}=> 刪除使用者
  • POST /users/{id}/notifications=> 為特定使用者建立通知
  • user.first_name
  • order.number

壞:
  • GET /getUser
  • POST /updateUser
  • POST /notification/user
  • order.ordernumber
  • user.firstName

 

10. 使用標準化的錯誤響應
除了使用指示請求結果(成功或錯誤)的 HTTP 狀態程式碼外,在返回錯誤時,始終使用標準化的錯誤響應,其中包含有關問題的更詳細資訊。消費者總是可以期待相同的結構並採取相應的行動。

// Request => GET /users/4TL011ax

// Response <= 404 Not Found
{
    "code": "user/not_found",
    "message": "A user with the ID 4TL011ax could not be found."
}
// Request => POST /users
{
    "name": "John Doe"
}

// Response <= 400 Bad Request
{
    "code": "user/email_required",
    "message": "The parameter [email] is required."
}

 

11. 返回建立的資源POST
POST在使用請求建立資源後返回建立的資源是個好主意。這一點很重要,因為返回的建立資源將反映底層資料來源的當前狀態,並將包含更新的資訊(例如生成的 ID)。

// Request: POST /users
{
    "email": "jdoe@averagecompany.com",
    "name": "John Doe"
}

// Response
{
    "id": "T9hoBuuTL4",
    "email": "jdoe@averagecompany.com",
    "name": "John Doe"
}

  

12. PATCH好於PUT
如前所述,PATCH請求應該對資源應用部分更新,而PUT完全替換現有資源。
而圍繞 PATCH 請求設計更新通常是個好主意,因為:

  • 當使用PUT只更新資源的一部分欄位時,仍然需要傳遞整個資源,這使得它更加網路密集且容易出錯
  • 允許任何欄位不受任何限制地更新也是相當危險的
  • 根據我的經驗,在實踐中幾乎不存在任何對資源進行完整更新有意義的用例
  • 想象一個order資源有一個id和一個state
  • 允許消費者更新state一個order
  • 狀態更改更有可能由另一個端點(例如/orders/{id}/fulfill)觸發

 

13. 儘可能具體
如上一節所述,在設計端點、命名欄位以及決定接受哪些請求和響應時,儘可能具體是一個好主意。如果一個PATCH請求只接受兩個欄位 (name和description),則不存在錯誤使用它和損壞資料的危險。
 

14.使用分頁
對所有返回資源集合並使用相同響應結構的請求進行分頁。使用page_numberand page_size(或類似的)來控制要檢索的塊。

// Request => GET /users?page_number=1&page_size=15

// Response <= 200 OK
{
    "page_number": 1,
    "page_size": 15,
    "count": 378,
    "data": [
        // resources
    ],
    "total_pages": 26,
    "has_previous_page": true,
    "has_next_page": true
}

 

15.允許擴充套件資源
允許消費者使用名為expand(或類似的)查詢引數載入相關資料。這對於避免往返和一次性載入特定操作所需的所有資料特別有用。

// Request => GET /users/T9hoBuuTL4?expand=orders&expand=orders.items

// Response <= 200 OK
{
  "id": "T9hoBuuTL4",
  "email": "jdoe@averagecompany.com",
  "name": "John Doe",
  "orders": [
    {
      "id": "Hy3SSXU1PF",
      "items": [
        {
          "name": "API course"
        },
        {
          "name": "iPhone 13"
        }
      ]
    },
    {
      "id": "bx1zKmJLI6",
      "items": [
        {
          "name": "SaaS subscription"
        }
      ]
    }
  ]
}



 

相關文章