基於工程經驗的『RESTful介面設計規範』

浮生若夢的程式設計發表於2018-05-18

前言

這篇文章,主要想總結自己在設計RESTful API的一系列經驗于思考。

有些規範可能與標準規範有所出入,但是所有的考量都是基於『減少重複工作,增加可讀性可維護性』而出發的。話說回來,我一直覺得 RESTful API 設計,確實沒有很明顯的公認規範(如果你是指發明者的那篇論文,估計沒多少人詳細閱讀過,而且其作者只是提出了一系列概念而已)。網上的教程,似乎都是千篇一律的(我嚴重懷疑:都是 “借鑑or拷貝” 阮一峰老師的那幾篇文章),僵硬而且呆板,有點教條化(從來沒有人懷疑它們嗎,反正我按照這些教條設計API,沒有感受到多少樂趣)。

以上,並不是全部否定,好東西要充分吸收,不好的東西,要融合自己的理解,加以改造。

對 RESTful API 的認識

說到這個,似乎又要談及『RESTful 是對資源的抽象』、『結合了HTTP 的特點』云云。不過這些都是一些沒用的套話,沒啥營養價值,而我想從另外的角度談論這個。

既然是 API,一般都符合 API 的一般模式:

ResultType ApiName(ParamType )

1. 介面引數,即形參。可以是 string,int,以及其他任意可以稱之為引數的東西
2. 介面返回值。可以是 string,int,以及其他任意可以稱之為返回值的東西
3. 介面名(簽名)
複製程式碼

我們來看看 RESTful 是如何對應上這個模式的:

HttpResponse URL(HttpRequest)

1. HttpRequest:包括請求頭,URL引數,請求body引數
2. HttpResponse: 包括響應頭,響應的body
複製程式碼

這樣來看,RESTful API 無非是一種特殊的API 而已,通用的 API 設計法則,同樣適合 RESTful,只不過非變換形式而已。

那麼我們大概有哪些比較通用的標準呢?大概有這些:

  1. 介面命名,必須做到清晰。一般來說,做到『動賓短語』即可。
  2. 介面數量,越少越好。三個不如兩個,兩個不如一個,一個不如沒有,最好的 API 就是『沒有API』。
  3. 有明確的輸入輸出。念念不忘必有迴響,總是有返回值,告訴呼叫端,我到底做了什麼,做得怎麼樣,即:反饋。

下面就來看看這些標準,是如何影響下文的內容的,:)。

URL設計,及其反模式

URL 就是介面簽名,而簽名必須做到清晰,沒有歧義。

有統一的字首 & 版本化

如果後端架構是服務化的,那麼有可能每個服務會對外提供公共的 RESTful API,那麼有個統一的字首格式,會比較好,比如:

/SERVICE_NAME/v1/users
or
/APP_NAME/v2/users
複製程式碼

儘量短小

同一份資源,可以有不同的路徑,去理解它。比如:

User -- 1:N --> Server-- 1:N --> Client ... 更加複雜的實體對映關係

1. /users/{user_id}/servers/{server_id}/clients
2. /clients

一般大家傾向於選項 1(但是實體關聯關係特複雜時,會縮短URL),
不過選項2 也是一個不錯的選擇,總而言之,口味問題吧。
複製程式碼

數量儘量少

介面數量越少越好,能合併的介面就儘量合併。比如,這樣的情況:

獲取使用者列表資訊:GET /users
獲取單個使用者資訊:GET /users/{id}

坦白說,獲取一個與獲取一批,似乎並沒有什麼語義上的差別,
但是後端的同學就不一樣了,他可能需要寫兩個 View Class。
所以只保留批量的介面,查詢一個時,用 URL 引數傳遞就行了。
複製程式碼

這樣的情況:

PUT /users/{id}
PUT /users

直接合併到一個介面裡面做就行了,PUT 一個 user與 PUT 一群 user,有啥本質的不同嗎?
複製程式碼

還有這樣極端的情況:

DELETE /users/{id}

刪除一個user與刪除一批user,有啥不同?
如果要一次刪除100 個 user,難道讓前端同學,調 100 次這個介面?
多一次呼叫,就多一次風險(如網路問題),
這個時候就別守著 RESTful 那些個教條了,介面的可用性、效率性,更加重要。

這個時候,不如設計成這樣(至於 DELETE 介面能不能傳Request body,這裡不討論):
POST /users_deletion
{
    "user_ids": [1, 2, 3, 4, 5]
}
複製程式碼

返回值設計

前面有說到,HttpResponse中,我們可以利用:

Response Headers
可以做少量文章,如自定義一個Header

Status Code
按照基本規範來,該404的404,該200的200

Response body
基本都是圍繞這個做文章
複製程式碼

Response body 既要能正常返回資訊,出錯了也要告訴出錯原因(錯誤碼),出錯詳情。所以我們大概可以設計成這樣:

{
    是否成功
    boolean "is_success":
    錯誤碼是多少
    number|null "err_code":
    錯誤資訊
    string|null "err_msg": 
    錯誤詳情(可選)
    string|null "err_detail":
    出錯的時哪個服務
    string|null "provider": 
    
    正常返回時的資料
    "response_data": {

    }
}
複製程式碼

這樣,前端呼叫 API ,就有章法可循了,不至於盲目。

欄位命名規範

沒有很明確的規範,但是儘量跟隨資料庫的風格,即:下劃線風格。 這樣,在 序列化整個 Model 時,也許會很方便。

其他規範

介面限流

參考 GitHub 的風格。

介面安全

這個沒法系統化,可以參考網路上相關文章。

相關文章