1. 程式人生 > >Restful API 的設計規範

Restful API 的設計規範

1. URI

URI 表示資源,資源一般對應伺服器端領域模型中的實體類。URI規範

  • 不用大寫;

  • 用中槓-而不用下槓_;

  • 引數列表要encode;

  • URI中的名詞表示資源集合,使用複數形式;

資源集合與單個資源 資源集合:

    /zoos //所有動物園
    /zoos/1/animals //id為1的動物園內的所有動物

單個資源:

    /zoos/1 //id為1的動物園
    /zoos/1;2;3 //id為1,2,3的動物園

避免層級過深的URI/在URI中表示層級,用於按實體關聯關係進行物件導航,一般跟進id導航; 過深的導航容易導致url膨脹,不易維護,如 GET /zoos/1/areas/3/animals/4

,儘量使用查詢引數代替路徑中的實體導航,如GET /animals?zoo=1&area=3;

對Composite資源的訪問 伺服器端的組合實體必須在uri中通過父實體的id導航訪問。

組合實體不是first-class的實體,它的生命週期完全依賴父實體,無法獨立存在,在實現上通常是對資料庫表中某些列的抽象,不直接對應表,也無id。一個常見的例子是 User — Address,Address是對User表中zipCode/country/city三個欄位的簡單抽象,無法獨立於User存在。必須通過User索引到Address:GET /user/1/addresses

2. Request

HTTP方法 通過標準HTTP方法對資源CRUD: GET: 查詢

    GET /zoos
    GET /zoos/1
    GET /zoos/1/employees

POST: 建立單個資源。POST一般向“資源集合”型URI發起; ··· javaascipt

POST /animals //新增動物
POST /zoos/1/employees //id為1的動物園的所有員工

PUT:更新單個資源(全量),客戶端提供完整的更新後的資源。與之對應的是 PATCH,PATCH 負責部分更新,客戶端提供要更新的那些欄位。PUT/PATCH一般向“單個資源”型uri發起

    PUT /animals/1
    PUT /zoos/1

DELETE:刪除

    DELETE /zoos/1/employees/2
    DELETE /zoos/1/employees/2;4;5
    DELETE /zoos/1/animals  //刪除id為1的動物園內的所有動物

HEAD / OPTION 用的不多,就不多解釋了。

安全性與冪等性 安全性:不會改變資源狀態,可以理解為只讀的; 冪等性:執行1次和執行N次,對資源狀態改變的效果是等價的。圖片描述

安全性和冪等性均不保證反覆請求能拿到相同的response。以 DELETE 為例,第一次DELETE返回200表示刪除成功,第二次返回404提示資源不存在,這是允許的。

複雜查詢 查詢可以捎帶以下引數:圖片描述

Bookmarker 經常使用的、複雜的查詢標籤化,降低維護成本。 如:

GET /trades?status=closed&sort=created,desc

快捷方式:

    GET /trades#recently-closed
    // 或者
    GET /trades/recently-closed

Format 只用以下常見的3種body format:

  1. Content-Type: application/json

    POST /v1/animal HTTP/1.1
    Host: api.example.org
    Accept: application/json
    Content-Type: application/json
    Content-Length: 24

    {   
      "name": "Gir",
      "animalType": "12"
    }
  1. Content-Type: application/x-www-form-urlencoded (瀏覽器POST表單用的格式)

    POST /login HTTP/1.1
    Host: example.com
    Content-Length: 31
    Accept: text/html
    Content-Type: application/x-www-form-urlencoded
    
    username=root&password=Zion0101
  1. Content-Type: multipart/form-data; boundary=—-RANDOM_jDMUxq4Ot5 (表單有檔案上傳時的格式)

Content Negotiation 資源可以有多種表示方式,如json、xml、pdf、excel等等,客戶端可以指定自己期望的格式,通常有兩種方式:

  1. http header Accept

Accept:application/xml;q=0.6,application/atom+xml;q=1.0

q為各項格式的偏好程度

  1. url中加檔案字尾:/zoo/1.json

3. Response

  1. 不要包裝

    response 的 body 直接就是資料,不要做多餘的包裝。錯誤示例:

    {
        "success":true,
        "data":{"id":1,"name":"xiaotuan"},
    }
  1. 各HTTP方法成功處理後的資料格式:圖片描述

  2. json格式的約定: 時間用長整形(毫秒數),客戶端自己按需解析(moment.js) 不傳null欄位

分頁response

    {
        "paging":{"limit":10,"offset":0,"total":729},
        "data":[{},{},{}...]
    }

4. 錯誤處理

  1. 不要發生了錯誤但給2xx響應,客戶端可能會快取成功的http請求;

  2. 正確設定http狀態碼,不要自定義;

  3. Response body 提供 1) 錯誤的程式碼(日誌/問題追查);2) 錯誤的描述文字(展示給使用者)。

對第三點的實現稍微多說一點: Java 伺服器端一般用異常表示 RESTful API 的錯誤。API 可能丟擲兩類異常:業務異常和非業務異常。業務異常由自己的業務程式碼丟擲,表示一個用例的前置條件不滿足、業務規則衝突等,比如引數校驗不通過、許可權校驗失敗。非業務類異常表示不在預期內的問題,通常由類庫、框架丟擲,或由於自己的程式碼邏輯錯誤導致,比如資料庫連線失敗、空指標異常、除0錯誤等等。 業務類異常必須提供2種資訊:

  1. 如果丟擲該類異常,HTTP 響應狀態碼應該設成什麼;

  2. 異常的文字描述;

在Controller層使用統一的異常攔截器:

  1. 設定 HTTP 響應狀態碼:對業務類異常,用它指定的 HTTP code;對非業務類異常,統一500;

  2. Response Body 的錯誤碼:異常類名

  3. Response Body 的錯誤描述:對業務類異常,用它指定的錯誤文字;對非業務類異常,線上可以統一文案如“伺服器端錯誤,請稍後再試”,開發或測試環境中用異常的 stacktrace,伺服器端提供該行為的開關。

常用的http狀態碼及使用場景:圖片描述

5. 服務型資源

除了資源簡單的CRUD,伺服器端經常還會提供其他服務,這些服務無法直接用上面提到的URI對映。如:

  1. 按關鍵字搜尋;

  2. 計算地球上兩點間的距離;

  3. 批量向用戶推送訊息;

可以把這些服務看成資源,計算的結果是資源的presentation,按服務屬性選擇合適的HTTP方法。 例如:

    GET /search?q=filter?category=file  搜尋
    GET /distance-calc?lats=47.480&lngs=-122.389&late=37.108&lnge=-122.448
    POST /batch-publish-msg
    [{"from":0,"to":1,"text":"abc"},{},{}...]

6. 非同步任務

對耗時的非同步任務,伺服器端接受客戶端傳遞的引數後,應返回建立成功的任務資源,其中包含了任務的執行狀態。客戶端可以輪訓該任務獲得最新的執行進度。

    // 提交任務:
    POST /batch-publish-msg
    [{"from":0,"to":1,"text":"abc"},{},{}...]
    
    // 返回:
    {"taskId":3,"createBy":"Anonymous","status":"running"}
    
    GET /task/3
    {"taskId":3,"createBy":"Anonymous","status":"success"}

如果任務的執行狀態包括較多資訊,可以把“執行狀態”抽象成組合資源,客戶端查詢該狀態資源瞭解任務的執行情況。

    提交任務:
    POST /batch-publish-msg
    [{"from":0,"to":1,"text":"abc"},{},{}...]
    
    返回:
    {"taskId":3,"createBy":"Anonymous"}
    
    GET /task/3/status
    {"progress":"50%","total":18,"success":8,"fail":1}

7. API演進

版本 常見的三種方式:

  1. 在uri中放版本資訊:GET /v1/users/1

  2. Accept Header:Accept: application/json+v1

  3. 自定義 Header:X-Api-Version: 1

用第一種,雖然沒有那麼優雅,但最明顯最方便。

URI失效 隨著系統發展,總有一些API失效或者遷移,對失效的API,返回404 not found 或 410 gone;對遷移的API,返回 301 重定向。