設計更好的Web API

知道你的身邊是不是經常有人抱著這樣的觀點 —— “接口能調通就行了,反正用戶看不到,別管是不是規範、是不是好看了”。事實上,API 設計本來就不是給用戶看的,而是給開發人員看的。

做好 API 設計並不需要耽誤很多的時間,但是把 API 設計得足夠規範,讓開發者一眼就能看出來每一個模型每一個字段的意義和用法,一方面可以減少大量的寫 API 文檔的工作量,可以很大程度上減少對接的雙方的溝通成本;另一方面好的 API 設計很容易做抽象,對接雙方都能複用大量的代碼。各個接口大致都是相同的,但是就是沒法抽象,必須每個都複製粘貼然後要做一些修改,這種情況也會得到改善。

先來看一些 Web API 設計的反例,大部分是實際工作中遇到的:

  • 不同的項目使用同一個名字
  • 用 api1、api2、api3… 來作為不同項目的 API 域名前綴
  • 接口和字段駝峰命名和下劃線命名混用
  • 用 2 和 4 來表示 to 和 for
  • 看上去是 RESTful,但是隻有 POST 和 GET
  • 不用 PATCH,針對數據每個字段的修改都要對應一個新的接口
  • 請求數據的格式 form-data 和 JSON 混用
  • 幾乎沒有資源型接口,接口全都是動詞 + model 名
  • 新增操作叫 addModel、createModel、upload、generateModel 等各種都有
  • 列表查詢操作叫 listModel、queryModel、modelList、list、getAll、all 等各種都有
  • 單個查詢操作叫 findModel、searchModel、get 各種都有
  • 刪除操作叫 removeModel、delete 都有,但是就是不用 HTTP 的 DELETE 方法
  • 雖然在 API 路徑裡有版本號,但是永遠都是 v1 不管 API 怎麼變化都不會改這個版本號

以上說了這麼多反面教材,接下來是一些我總結的一些好的 Web API 設計模式,主要是 HTTP RESTful 風格的 API。如果是用 GraphQL 或者一些雲廠商的 SDK 比如 AWS Web SDK、Firebase Web SDK 等類似於 RPC 的模式,也有部分可以參考。

命名規範

  • 使用簡單、不衝突、不重複、有一定規律或模式的一個單詞或者兩個單詞作為項目的命名,這個命名會用在接口的域名前綴、namespace 等多個地方,也可以叫做“內部代號”或者 Code name。這個 鏈接 舉了很多這樣的例子。
  • 用準確的英文(而非拼音、數字等)名詞來表示數據模型。淘寶前端團隊的這篇 《從達標到卓越 — API 設計之道》 裡有更詳細的描述。
  • 作為數據模型的單詞應該是能夠準確描述這個數據模型的含義的,如果不能用準確的單詞表示,開發團隊應該有一份統一的單詞表(glossary)來做約定。
  • 對於單個數據實例和多個數據的集合,是有單複數區別的。但是這種事情最好交給一個統一的單複數的庫去處理,比如 Rails 裡面默認就集成了單複數處理,另外比如 pluralize 之類的庫也能做類似的事情。

命名風格

我推薦的風格是數據庫字段、API 接口名、字段名等使用小寫字母 + 下劃線做分割的模式,也叫 snake_case,以下解釋為什麼:

  • 接口名和字段名都直接會在 URL 裡訪問到,URL 雖然是大小寫敏感的,但是因為 URL 和域名、文件名等都有關係,而且早期瀏覽器對 URL 是大小寫不敏感的,所以有個不成文的約定就是 URL 默認就是全小寫,除某些特定情況外(比如短鏈接使用大小寫混合來保證用較少的位數包含較多的信息)。
  • 不用連字符(或者叫減號)做分割,因為數據字段會映射到編程語言裡,而大部分編程語言裡的連字符是關鍵字,大部分情況下是減法操作符或者表示負數的符號。
  • 相比於駝峰命名,下劃線分割便於分割和合並,大部分編程語言裡都有原生的字符串分割和拼接,相比之下駝峰命名做這樣的操作就要複雜一些了。
  • 駝峰命名在 Java 和 JavaScript 裡很常見,但是在其他一些編程語言裡並不是主流風格。

字段設計

字段可以分為數據實例字段和數據模型字段。數據實例字段場景的就是 ID、創建時間、修改時間等:

  • 用來做 ID 字段名就叫 id,而不能叫 pid、photo_id、photoId 之類的。因為 id 是用來確定數據實例唯一性的標誌,不是跟具體某個模型有關係的字段。
  • 關鍵時間用 create_at 和 update_at 表示創建時間和修改時間,為什麼不用 create_time,因為 time 有歧義,除了“時間”還有“次數”的含義。

以及數據模型字段的設計風格:

  • 儘量用簡單、單一的單詞作為字段,而非隱晦而複雜的單詞。
  • 業界通用的縮寫(主要是 acronyms,即首字母縮寫)比如 url、http 等可以用來作為字段名。其他的縮寫(主要是 abbreviation,即單詞縮寫)比如 desc、auth、cal、func 等儘量用完整形式以降低產生歧義的可能性。
  • 外鍵或者關聯字段的命名也要統一,比如如果是 SQL,外鍵就叫 user_id,而 NoSQL 如果是把 user 實例直接複製過來了,那麼關聯字段應該叫 user。

路由設計

我的建議是參考 Rails 的路由設計。Rails 是 MVC Web 後端框架的開山鼻祖,很多其他語言的框架比如 Laravel、Django、Spring 等或多或少都有“借鑑” Rails 的成分。以下是我在 Rails 官方文檔上關於資源型數據的路由的設計規範的基礎上增加了部分內容的表格:

HTTP Verb Path Controller#Action Usage 前端&後端 GET /photos photos#index 展示照片列表 前端 GET /photos/new photos#new 展示“新建照片”表單 後端 POST /photos photos#create 提交新建表單 前端&後端 GET /photos/:id photos#show 展示某個 id 的照片 前端 GET /photos/:id/edit photos#edit 展示編輯某個 id 的照片的表單 後端 PATCH/PUT /photos/:id photos#update 提交編輯表單 後端 DELETE /photos/:id photos#destroy 提交刪除某個 id 照片的操作 為什麼要這樣做:

  • 充分利用了 HTTP 的幾個基本操作,且符合 RESTful 風格。
  • 創建和刪除的操作叫 create/destroy 而不叫 new/delete,因為操作的名字在前後端都有可能以函數名的形式體現,這就避免了在一些編程語言裡跟 new/delete 和關鍵字衝突。
  • 可以把 photos 替換成任何其他資源模型的名字,不管是前端後端,所有操作的代碼都能直接複用或抽象出來。
  • 保持對單一資源操作路由的簡單性,多個資源關聯的操作也能按照同樣的思路很自然的設計。
  • 通過對固定模式的約定,不需要再去寫複雜的接口文檔,這個約定本身就是文檔了。尤其是前後端分離且是不同的人開發的時候這一點尤其有用。

總結

設計好的 Web API 並沒有想象的那麼難,需要設計者去參考自己所用的那個框架的設計規範,以及參考流行框架的設計思路。如果遇到現有規範裡沒有的情況而需要自己設計的時候,一方面是參考主流框架、主流公司的 API 的做法,另一方面是要把“抽象”和“易讀”落實到設計中去。

鏈接:https://www.jianshu.com/p/17ca21d21f67


分享到:


相關文章: