天天看點

編寫 Node.js Rest API 的 10 個最佳實踐

node.js 除了用來編寫 web 應用之外,還可以用來編寫 api 服務,我們在本文中會介紹編寫 node.js rest api 的最佳實踐,包括如何命名路由、進行認證和測試等話題,内容摘要如下:

正确使用 http method 和路由

正确的使用 http 狀态碼

使用 http header 來發送中繼資料

為 rest api 挑選合适的架構

要對 api 進行黑盒測試

使用基于 jwt 的無狀态的認證機制

學會使用條件請求機制

擁抱接口調用頻率限制(rate-limiting)

編寫良好的 api 文檔

對 api 技術演化保持關注

  試想你正要建構一個 api 用來建立、更新、擷取、删除使用者,對于這些操作,http 規範裡面已經有了現成的操作:post、put、get、delete,建議直接使用他們來描述接口的行為。

  至于路由的命名,應該使用名詞或名詞性短語來作為資源辨別符,比如上文提到的使用者管理的例子,路由就應該長這樣:

post /users 或者 put /users/:id 用來建立新使用者;

get /users 用來擷取使用者清單;

get /users/:id 用來擷取單個使用者;

patch /users/:id 用來更新使用者資訊;

delete /users/:id 用來删除使用者;

  如果伺服器端在請求處理的過程中出錯了,你必須設定正确的響應狀态碼,具體如下:

2xx,表示一切正常;

3xx,表示資源位置已經更改;

4xx,表示因為用戶端錯誤而導緻請求無法被處理,比如參數校驗沒通過;

5xx,表示因為伺服器錯誤導緻請求無法被處理,比如服務端抛了異常;

  如果你使用 express,設定狀态碼非常簡單:res.status(500).send({ error: 'internal server error happend' }),如果使用了 restify,也是類似的:res.status(201)。

  如果想看完整的 http 狀态碼,​​點選這裡​​。

  如果想要發送關于響應體資料的中繼資料,可以使用 header ,header 可以包含的常見中繼資料包括如下幾類:

分頁資訊;

頻率限制資訊;

認證資訊;

  如果你需要在 header 中發送自定義的中繼資料,最好的做法是在 header 名稱前面加 x,例如,需要發送 csrf token 的時候,實際的 header 應該命名為:x-csrf-token,然而,這種 header 在 ​​rfc 6648​​ 中已經被廢棄了。api 在設定自定義 header 的時候還要盡可能避免命名沖突,比如為了達到這個目的openstack 為所有 api 的自定義 header 都加上了 openstack 的字首:

  需要注意的是,雖然 http 規範中沒有規定 header 的大小,但是 node.js 中 header 的大小被限制在了 80kb。官方原文如下:

不要讓 http header ,包括其中狀态碼那行的整體大小超過 http_max_header_size,這樣做的目的是為了防禦基于 header 的 ddos 攻擊。​​點選這裡​​

  根據你的實際場景挑選合适的架構是非常重要的,node.js 中的架構大緻介紹如下:

  ​​express​​、​​koa​​、​​hapi​​ 主要是用來建構浏覽器 web 應用,因為他們都支援服務端模闆渲染,雖然這隻是他們衆多功能中的一個。如果你的應用需要提供使用者界面,那麼這三個就是不錯的選擇。

  而 ​​restify​​ 是專門用來建立符合 rest 規範的服務的,他誕生的目的就是幫你建構嚴格意義上的、可維護的 api 服務。restify 内置了所有請求處理函數的 ​​dtrace​​ 支援。并且已經被 ​​npm​​ 和 ​​netflix​​ 用來在生産環境提供重要的服務。

  測試 api 的最好辦法是對他們進行黑盒測試,黑盒測試是一種不關心應用内部結構和工作原理的測試方法,測試時系統任何部分都不應該被 mock。

  ​​supertest​​ 是可以用來對接口進行黑盒測試的子產品之一,下面是基于測試架構 ​​mocha​​ 編寫的一個測試用例,該用例的目的是檢查接口是否能傳回單條的使用者資料:

  可能有人會問:api 服務所連接配接的資料庫裡面的資料是如何寫進去的呢?

  通常來說,你寫測試的時候,要盡可能不對系統狀态做假設,然而在某些場景下,你需要準确的知道系統目前所處的狀态以增加更多的斷言來提高測試覆寫率。如果你有這種需求,你可以試用如下的方法對資料庫進行預填充:

選擇生産環境資料的子集來運作黑盒測試;

運作黑盒測試之前把手工構造的資料填充到資料庫中。

  此外,有了黑盒測試并不意味着不需要單元測試,針對 api 的​​單元測試​​還是需要編寫的。

  因為 rest api 必須是無狀态的,是以認證機制也需要是無狀态的,而基于 jwt(json web token) 的認證機制是無狀态認證機制中的最佳解決方案。

  jwt 的認證機制包含三部分:

header:包含 token 的類型和雜湊演算法;

payload:包含聲明資訊;

signature:jwt 實際上并不是對 payload 進行加密,隻是對其做了簽名;

  為 api 添加基于 jwt 的認證機制也非常的簡單,比如下面的代碼:

  有了如上的代碼,你的 api 就有了 jwt 的保護。如果要通路這種被保護的接口,需要使用 authorization header 來提供 token,比如:

  你可能注意到了,jwt 子產品并不依賴任何資料存儲層,這是因為 token 本身是可以單獨被校驗的,token 裡面的 payload 甚至可以包含 token 的簽名時間、有效期限。

  此外,你還需要確定,所有的 api 接口隻能通過更安全的 https 連結來通路。

  條件請求機制是基于不同的 header 表現出不同的行為的機制,可以認為這些 header 就是請求處理方式的先決條件,如果條件滿足,請求處理方式就會有所不同。

  可以利用這些 header 檢測伺服器上的資源版本是否比對特定的資源版本,這些 header 的取值可以是如下的内容:

資源的最後修改時間;

資源的标簽(随資源變化而變化);

  具體來說:

last-modified:辨別資源的最新修改時間;

etag:辨別資源的标簽;

if-modified-since:結合 last-modified header 使用;

if-non-match:結合 etag 使用;

  下面來看一個實際的例子:

  用戶端不知道 doc 資源的任何版本,是以請求時即不能提供 if-modified-since,也不能提供 if-non-match 兩個 header,然後服務端在響應中會增加 etag 和 last-modified 兩個 header。

  nodejs-resftul-api-with-conditional-request-without-previous-versions.png

  接下來,用戶端再次請求相同的資源的時候,就可以帶上 if-modified-since 和 if-non-match 這兩個 header 了,然後如果伺服器端會檢查資源是否修改,如果沒有修改,直接傳回 304 - not modified 狀态碼,而不重複發送資源的内容。

  nodejs-resftul-api-with-conditional-request-with-previous-versions.png

  頻率限制是用來控制調用方有對接口發起請求的次數,為了讓你的 api 使用者知道他們還剩下多少餘額,可以設定下面的 header:

x-rate-limit-limit:特定時間段内允許的最多請求次數;

x-rate-limit-remaining:特定時間段内剩餘的請求次數;

x-rate-limit-reset:什麼時候請求頻率限制次數會重置;

  大多數的 web 架構都支援上面這些 header,如果内置不支援,也可以找到插件來支援,比如,如果你使用了 koa,可以使用 ​​koa-rate-limit​​。

  需要注意的是,不同的 api 服務提供商頻率限制的時間窗差異會很大,比如 github 是 60 分鐘,而 twitter 是 15 分鐘。

  編寫 api 的目的當然是讓别人使用并受益,提供良好的接口文檔至關重要。下面這兩個開源項目可以幫你建立 api 文檔:

​​api blueprint​​

​​swagger​​

  如果你願意使用第三方文檔服務商,可以考慮 ​​apiary​​。

  過去幾年中,api 技術方案領域出現了兩種新的查詢語言,分别是 facebook 的 graphql 和 netflix 的 falcor,為什麼需要他們呢?

  試想這種 api 接口請求:/org/1/space/2/docs/1/collaborators?include=email&page=1&limit=10,類似的情況會讓 api 很快失控,如果你希望所有接口能傳回類似的響應格式,那麼 graphql 和 falcor 就能幫你解決這個問題。

  關于 graphql:

graphql 是一種用于 api 的查詢語言,也是一種基于現有資料處理資料查詢的運作時。graphql 為您的 api 中的資料提供了一個完整和可了解的描述,使使用者能夠準确地詢問他們需要什麼,使得随着時間推移的 api 演化更容易,graphql 還有強大的開發工具支援。 到​​這裡​​閱讀更多。

  關于 falcor:

falcor 是支撐着 netflix ui 的創新資料平台。falcor 允許你将所有後端資料模組化為 node.js 服務商的單個虛拟 json 對象。在用戶端可以使用熟悉的 javascript 操作、處理遠端json對象。如果你知道你的資料,你就知道你的 api 長啥樣。 到​​這裡​​閱讀更多。

  如果你正在開發 rest api 或者準備改進老版本的 api,這裡收集了幾個線上上提供服務、設計優秀并且非常直接借鑒的 api:

​​github api​​

​​twilio api​​

​​stripe api​​

​​digital ocean api​​

  希望讀到這裡的同學對如何用 node.js 編寫良好的 api 有更好的了解,如果有建議,歡迎評論中提出。

繼續閱讀