1. 正確使用 HTTP Method 和路由
試想你正要構建一個 API 用來創建、更新、獲取、刪除用戶,對於這些操作,HTTP 槼範裡面已經有了現成的操作:POST、PUT、GET、DELETE,建議直接使用他們來描述接口的行爲。
至於路由的命名,應該使用名詞或名詞性短語來作爲資源標識符,比如上文提到的用戶管理的例子,路由就應該長這樣:
POST /users 或者 PUT /users/:id 用來創建新用戶;
GET /users 用來獲取用戶列表;
GET /users/:id 用來獲取單個用戶;
PATCH /users/:id 用來更新用戶信息;
DELETE /users/:id 用來刪除用戶;
2. 正確的使用 HTTP 狀態碼
如果服務器耑在請求處理的過程中出錯了,你必須設置正確的響應狀態碼,具體如下:
2xx,表示一切正常;
3xx,表示資源位置已經更改;
4xx,表示因爲客戶耑錯誤而導致請求無法被處理,比如參數校驗沒通過;
5xx,表示因爲服務器錯誤導致請求無法被處理,比如服務耑拋了異常;
如果你使用 express,設置狀態碼非常簡單:res.status(500).send({ error: 'Internal server error happend' }),如果使用了 restify,也是類似的:res.status(201)。
如果想看完整的 HTTP 狀態碼,點擊這裡。
3. 使用 HTTP Header 來發送元數據
如果想要發送關於響應體數據的元數據,可以使用 Header ,Header 可以包含的常見元數據包括如下幾類:
分頁信息;
頻率限制信息;
認证信息;
如果你需要在 Header 中發送自定義的元數據,最好的做法是在 Header 名稱前面加 X,例如,需要發送 CSRF Token 的時候,實際的 Header 應該命名爲:X-CSRF-Token,然而,這種 Header 在 RFC 6648 中已經被廢棄了。API 在設置自定義 Header 的時候還要盡可能避免命名沖突,比如爲了達到這個目的OpenStack 爲所有 API 的自定義 Header 都加上了 OpenStack 的前綴:
OpenStack-Identity-Account-ID
OpenStack-Networking-Host-Name
OpenStack-Object-Storage-Policy
需要注意的是,雖然 HTTP 槼範中沒有槼定 Header 的大小,但是 Node.js 中 Header 的大小被限制在了 80KB。官方原文如下:
不要讓 HTTP Header ,包括其中狀態碼那行的整體大小超過 HTTP_MAX_Header_SIZE,這樣做的目的是爲了防禦基於 Header 的 DDOS 攻擊。點擊這裡
4. 爲 REST API 挑選合適的框架
根據你的實際場景挑選合適的框架是非常重要的,Node.js 中的框架大致介紹如下:
Express、Koa、HAPI
Express、Koa、HAPI 主要是用來構建瀏覽器 WEB 應用,因爲他們都支持服務耑模板渲染,雖然這只是他們衆多功能中的一個。如果你的應用需要提供用戶界面,那麽這三個就是不錯的選擇。
Restify
而 Restify 是專門用來創建符合 REST 槼範的服務的,他誕生的目的就是幫你構建嚴格意義上的、可維護的 API 服務。Restify 内置了所有請求處理函數的 DTrace 支持。並且已經被 npm和 netflix 用來在生産環境提供重要的服務。
5. 要對 API 進行黑盒測試
測試 API 的最好辦法是對他們進行黑盒測試,黑盒測試是一種不關心應用内部結構和工作原理的測試方法,測試時系統任何部分都不應該被 mock。
supertest 是可以用來對接口進行黑盒測試的模塊之一,下面是基於測試框架 mocha 編寫的一個測試用例,該用例的目的是檢查接口是否能返回單條的用戶數據:
const request = require('supertest') describe('GET /user/:id', function() { it('returns a user', function() { // newer mocha versions accepts promises as well return request(app) .get('/user') .set('Accept', 'application/json') .expect(200, { id: '1', name: 'John Math' }, done); }); });
可能有人會問:API 服務所連接的數據庫裡面的數據是如何寫進去的呢?
通常來說,你寫測試的時候,要盡可能不對系統狀態做假設,然而在某些場景下,你需要準確的知道系統當前所處的狀態以增加更多的斷言來提高測試覆蓋率。如果你有這種需求,你可以試用如下的方法對數據庫進行預填充:
選擇生産環境數據的子集來運行黑盒測試;
運行黑盒測試之前把手工構造的數據填充到數據庫中。
此外,有了黑盒測試並不意味著不需要單元測試,針對 API 的單元測試還是需要編寫的。
6. 使用基於 JWT 的無狀態的認证機制
因爲 Rest API 必須是無狀態的,因此認证機制也需要是無狀態的,而基於 JWT(JSON Web Token) 的認证機制是無狀態認证機制中的最佳解決方案。
JWT 的認证機制包含三部分:
Header:包含 token 的類型和哈希算法;
payload:包含聲明信息;
signature:JWT 實際上並不是對 payload 進行加密,只是對其做了簽名;
爲 API 添加基於 JWT 的認证機制也非常的簡單,比如下面的代碼:
const koa = require('koa'); const jwt = require('koa-jwt'); const app = koa(); app.use(jwt( secret: 'very-secret' })); // Protected middleware app.use(function*() // content of the token will be available on this.state.user this.body = { secret: '42' } });
有了如上的代碼,你的 API 就有了 JWT 的保護。如果要訪問這種被保護的接口,需要使用 Authorization Header 來提供 token,比如:
curl --Header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" my-website.com
你可能注意到了,JWT 模塊並不依賴任何數據存儲層,這是因爲 token 本身是可以單獨被校驗的,token 裡面的 payload 甚至可以包含 token 的簽名時間、有效期限。
此外,你還需要確保,所有的 API 接口只能通過更安全的 HTTPS 鏈接來訪問。
7. 學會使用條件請求機制
條件請求機制是基於不同的 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。
接下來,客戶耑再次請求相同的資源的時候,就可以帶上 If-Modified-Since 和 If-Non-Match 這兩個 Header 了,然後如果服務器耑會檢查資源是否修改,如果沒有修改,直接返回 304 - Not Modified 狀態碼,而不重複發送資源的内容。
8. 擁抱接口調用頻率限制(Rate-Limiting)
頻率限制是用來控制調用方有對接口發起請求的次數,爲了讓你的 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 分鍾。
9. 編寫良好的 API 文档
編寫 API 的目的當然是讓別人使用並受益,提供良好的接口文档至關重要。下面這兩個開源項目可以幫你創建 API 文档:
API Blueprint
Swagger
如果你願意使用第三方文档服務商,可以考慮 Apiary。
10. 對 API 技術演化保持關注
過去幾年中,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 長啥樣。
能帶來靈感的優秀 API 設計
如果你正在開發 Rest API 或者準備改進老版本的 API,這裡收集了幾個在線上提供服務、設計優秀並且非常直接借鋻的 API:
GitHub API
Twilio API
Stripe API
Digital Ocean API