服務端指南 | 良好的 API 設計指南

設計一套良好的 API 接口。
原文地址:服務端指南 | 良好的 API 設計指南
博客地址:blog.720ui.com/數據庫

版本號

在 RESTful API 中,API 接口應該儘可能兼容以前的版本。可是,在實際業務開發場景中,可能隨着業務需求的不斷迭代,現有的 API 接口沒法支持舊版本的適配,此時若是強制升級服務端的 API 接口將致使客戶端舊有功能出現故障。實際上,Web 端是部署在服務器,所以它能夠很容易爲了適配服務端的新的 API 接口進行版本升級,然而像 Android 端、IOS 端、PC 端等其餘客戶端是運行在用戶的機器上,所以當前產品很難作到適配新的服務端的 API 接口,從而出現功能故障,這種狀況下,用戶必須升級產品到最新的版本才能正常使用。json

爲了解決這個版本不兼容問題,在設計 RESTful API 的一種實用的作法是使用版本號。通常狀況下,咱們會在 url 中保留版本號,並同時兼容多個版本。api

【GET】  /v1/users/{user_id}  // 版本 v1 的查詢用戶列表的 API 接口
【GET】  /v2/users/{user_id}  // 版本 v2 的查詢用戶列表的 API 接口複製代碼

如今,咱們能夠不改變版本 v1 的查詢用戶列表的 API 接口的狀況下,新增版本 v2 的查詢用戶列表的 API 接口以知足新的業務需求,此時,客戶端的產品的新功能將請求新的服務端的 API 接口地址。雖然服務端會同時兼容多個版本,可是同時維護太多版本對於服務端而言是個不小的負擔,由於服務端要維護多套代碼。這種狀況下,常見的作法不是維護全部的兼容版本,而是隻維護最新的幾個兼容版本,例如維護最新的三個兼容版本。在一段時間後,當絕大多數用戶升級到較新的版本後,廢棄一些使用量較少的服務端的老版本API 接口版本,並要求使用產品的很是舊的版本的用戶強制升級。bash

注意的是,「不改變版本 v1 的查詢用戶列表的 API 接口」主要指的是對於客戶端的調用者而言它看起來是沒有改變。而實際上,若是業務變化太大,服務端的開發人員須要對舊版本的 API 接口使用適配器模式將請求適配到新的API 接口上。服務器

資源路徑

RESTful API 的設計以資源爲核心,每個 URI 表明一種資源。所以,URI 不能包含動詞,只能是名詞。注意的是,形容詞也是可使用的,可是儘可能少用。通常來講,不論資源是單個仍是多個,API 的名詞要以複數進行命名。此外,命名名詞的時候,要使用小寫、數字及下劃線來區分多個單詞。這樣的設計是爲了與 json 對象及屬性的命名方案保持一致。例如,一個查詢系統標籤的接口能夠進行以下設計。微信

【GET】  /v1/tags/{tag_id}複製代碼

同時,資源的路徑應該從根到子依次以下。restful

/{resources}/{resource_id}/{sub_resources}/{sub_resource_id}/{sub_resource_property}複製代碼

咱們來看一個「添加用戶的角色」的設計,其中「用戶」是主資源,「角色」是子資源。app

【POST】  /v1/users/{user_id}/roles/{role_id} // 添加用戶的角色複製代碼

有的時候,當一個資源變化難以使用標準的 RESTful API 來命名,能夠考慮使用一些特殊的 actions 命名。ide

/{resources}/{resource_id}/actions/{action}複製代碼

舉個例子,「密碼修改」這個接口的命名很難徹底使用名詞來構建路徑,此時能夠引入 action 命名。性能

【PUT】  /v1/users/{user_id}/password/actions/modify // 密碼修改複製代碼

請求方式

能夠經過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對服務端的資源進行操做。其中,GET 用於查詢資源,POST 用於建立資源,PUT 用於更新服務端的資源的所有信息,PATCH 用於更新服務端的資源的部分信息,DELETE 用於刪除服務端的資源。

這裏,筆者使用「用戶」的案例進行回顧經過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對服務端的資源進行操做。

【GET】          /users                # 查詢用戶信息列表
【GET】          /users/1001           # 查看某個用戶信息
【POST】         /users                # 新建用戶信息
【PUT】          /users/1001           # 更新用戶信息(所有字段)
【PATCH】        /users/1001           # 更新用戶信息(部分字段)
【DELETE】       /users/1001           # 刪除用戶信息複製代碼

查詢參數

RESTful API 接口應該提供參數,過濾返回結果。其中,offset 指定返回記錄的開始位置。通常狀況下,它會結合 limit 來作分頁的查詢,這裏 limit 指定返回記錄的數量。

【GET】  /{version}/{resources}/{resource_id}?offset=0&limit=20複製代碼

同時,orderby 能夠用來排序,但僅支持單個字符的排序,若是存在多個字段排序,須要業務中擴展其餘參數進行支持。

【GET】  /{version}/{resources}/{resource_id}?orderby={field} [asc|desc]複製代碼

爲了更好地選擇是否支持查詢總數,咱們可使用 count 字段,count 表示返回數據是否包含總條數,它的默認值爲 false。

【GET】  /{version}/{resources}/{resource_id}?count=[true|false]複製代碼

上面介紹的 offset、 limit、 orderby 是一些公共參數。此外,業務場景中還存在許多個性化的參數。咱們來看一個例子。

【GET】  /v1/categorys/{category_id}/apps/{app_id}?enable=[1|0]&os_type={field}&device_ids={field,field,…}複製代碼

注意的是,不要過分設計,只返回用戶須要的查詢參數。此外,須要考慮是否對查詢參數建立數據庫索引以提升查詢性能。

狀態碼

使用適合的狀態碼很重要,而不該該所有都返回狀態碼 200,或者隨便亂使用。這裏,列舉筆者在實際開發過程當中經常使用的一些狀態碼,以供參考。

狀態碼 描述
200 請求成功
201 建立成功
400 錯誤的請求
401 未驗證
403 被拒絕
404 沒法找到
409 資源衝突
500 服務器內部錯誤

異常響應

當 RESTful API 接口出現非 2xx 的 HTTP 錯誤碼響應時,採用全局的異常結構響應信息。

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
    "code": "INVALID_ARGUMENT",
    "message": "{error message}",
    "cause": "{cause message}",
    "request_id": "01234567-89ab-cdef-0123-456789abcdef",
    "host_id": "{server identity}",
    "server_time": "2014-01-01T12:00:00Z"
}複製代碼

請求參數

在設計服務端的 RESTful API 的時候,咱們還須要對請求參數進行限制說明。例如一個支持批量查詢的接口,咱們要考慮最大支持查詢的數量。

【GET】     /v1/users/batch?user_ids=1001,1002      // 批量查詢用戶信息
參數說明
- user_ids: 用戶ID串,最多容許 20 個。複製代碼

此外,在設計新增或修改接口時,咱們還須要在文檔中明確告訴調用者哪些參數是必填項,哪些是選填項,以及它們的邊界值的限制。

【POST】     /v1/users                             // 建立用戶信息
請求內容
{
    "username": "lgz",                 // 必填, 用戶名稱, max 10
    "realname": "梁桂釗",               // 必填, 用戶名稱, max 10
    "password": "123456",              // 必填, 用戶密碼, max 32
    "email": "lianggzone@163.com",     // 選填, 電子郵箱, max 32
    "weixin": "LiangGzone",            // 選填,微信帳號, max 32
    "sex": 1                           // 必填, 用戶性別[1-男 2-女 99-未知]
}複製代碼

響應參數

針對不一樣操做,服務端向用戶返回的結果應該符合如下規範。

【GET】     /{version}/{resources}/{resource_id}      // 返回單個資源對象
【GET】     /{version}/{resources}                    // 返回資源對象的列表
【POST】    /{version}/{resources}                    // 返回新生成的資源對象
【PUT】     /{version}/{resources}/{resource_id}      // 返回完整的資源對象
【PATCH】   /{version}/{resources}/{resource_id}      // 返回完整的資源對象
【DELETE】  /{version}/{resources}/{resource_id}      // 狀態碼 200,返回完整的資源對象。
                                                      // 狀態碼 204,返回一個空文檔複製代碼

若是是單條數據,則返回一個對象的 JSON 字符串。

HTTP/1.1 200 OK
{
    "id" : "01234567-89ab-cdef-0123-456789abcdef",
    "name" : "example",
    "created_time": 1496676420000,
    "updated_time": 1496676420000,
    ...
}複製代碼

若是是列表數據,則返回一個封裝的結構體。

HTTP/1.1 200 OK
{
    "count":100,
    "items":[
        {
            "id" : "01234567-89ab-cdef-0123-456789abcdef",
            "name" : "example",
            "created_time": 1496676420000,
            "updated_time": 1496676420000,
            ...
        },
        ...
    ]
}複製代碼

一個完整的案例

最後,咱們使用一個完整的案例將前面介紹的知識整合起來。這裏,使用「獲取用戶列表」的案例。

【GET】     /v1/users?[&keyword=xxx][&enable=1][&offset=0][&limit=20] 獲取用戶列表
功能說明:獲取用戶列表
請求方式:GET
參數說明
- keyword: 模糊查找的關鍵字。[選填]
- enable: 啓用狀態[1-啓用 2-禁用]。[選填]
- offset: 獲取位置偏移,從 0 開始。[選填]
- limit: 每次獲取返回的條數,缺省爲 20 條,最大不超過 100。 [選填]
響應內容
HTTP/1.1 200 OK
{
    "count":100,
    "items":[
        {
            "id" : "01234567-89ab-cdef-0123-456789abcdef",
            "name" : "example",
            "created_time": 1496676420000,
            "updated_time": 1496676420000,
            ...
        },
        ...
    ]
}
失敗響應
HTTP/1.1 403 UC/AUTH_DENIED
Content-Type: application/json
{
    "code": "INVALID_ARGUMENT",
    "message": "{error message}",
    "cause": "{cause message}",
    "request_id": "01234567-89ab-cdef-0123-456789abcdef",
    "host_id": "{server identity}",
    "server_time": "2014-01-01T12:00:00Z"
}
錯誤代碼
- 403 UC/AUTH_DENIED    受權受限複製代碼

(完)

更多精彩文章,盡在「服務端思惟」微信公衆號!

相關文章
相關標籤/搜索