再次強調一些基本的RESTful API設計原則:
1、圍繞資源組織API設計
2、根據HTTP方法定義API操作
3、媒體類型符合HTTP語義Content-Type
4、支持大型二進制資源的部分響應,資源可能包含大型二進制字段,如文件或圖像。為了克服由不可靠和間歇性連接引起的問題,并縮短響應時間,請考慮以塊方式檢索此類資源。
5、版本化RESTful API
6、使用過濾器,防止返回所有數據
7、同步設計API的安全性
8、為用戶提供API文檔,可借助API工具實現自動化的 API文檔生成。
9、為用戶提供SDK,可借助API工具實現自動的 API客戶端SDK生成。
HTTP 1.1 規(guī)范RFC2616是一份長達 54,121 個字的厚重文檔。以下是該規(guī)范中可能影響您的 API 設計的一些選定項目:
#1. 冪等方法– GET、HEAD、PUT、DELETE、OPTIONS 和 TRACE 均旨在成為冪等操作;也就是說,“N > 0 個相同請求的副作用與單個請求相同。”(RFC2616 §9.1.2)
#2. 身份驗證– 大多數 API 都需要一種方法來識別和驗證訪問 API 的用戶。HTTP為此提供了授權標頭 ( RFC2616 §14.8 )。RFC2617 指定了特定的身份驗證方案,包括最常見的 HTTP 基本身份驗證。
> 許多流行的 API 都使用 HTTP 基本身份驗證,以 API 密鑰作為用戶名或密碼。這是一種簡單有效的身份驗證機制。
>
> 為了準確實現 HTTP 身份驗證,如果請求因缺乏身份驗證而未被允許,您應提供 401 狀態(tài)代碼和 WWW-Authenticate 標頭。許多客戶端在后續(xù)請求中發(fā)送 Authorization 標頭之前都需要此響應。
#3. 201 Created – 使用“201 Created”響應代碼表示請求已成功處理并導致創(chuàng)建新資源。201 響應可以在 Location 標頭中包含新資源 URI。(RFC2616 §10.2.2)
#4. 202 已接受– 使用“202 已接受”響應代碼表示請求有效且將被處理,但尚未完成。通常,這用于服務器端后臺可能存在處理隊列的情況。(RFC2616 §10.2.3)
#5. 4xx 與 5xx 狀態(tài)代碼– 4xx 與 5xx 狀態(tài)代碼之間存在重要區(qū)別:4xx 代碼用于指示客戶端錯誤,而 5xx 代碼指示服務器端錯誤。正確使用這些狀態(tài)代碼類可以幫助應用程序開發(fā)人員了解他們是否做錯了什么(4xx)或某些東西壞了(5xx)。(RFC2616 §6.1.1)(編輯:閱讀“404 Not Found”真的是客戶端錯誤嗎?了解有關區(qū)別的更多信息。)
#6. 410 Gone – “410 Gone” 響應代碼是一個未充分利用的響應代碼,它通知客戶端該 URL 上曾經存在某個資源,但現在已不存在。這可以在您的 API 中用于指示已刪除、已存檔或已過期的項目。(RFC2616 §10.4.11)
#7. 期望:100-Continue – 如果 API 客戶端即將發(fā)送具有大型實體主體的請求,例如 POST、PUT 或 PATCH,它們可以在 HTTP 標頭中發(fā)送“期望:100-Continue”,并等待“100 Continue”響應后再發(fā)送實體主體。這允許 API 服務器在浪費帶寬返回錯誤響應(例如 401 或 403)之前驗證請求的大部分有效性。支持此功能并不常見,但它可以提高 API 響應能力并在某些情況下減少帶寬。(RFC2616 §8.2.3)
#8. 保持連接– 為多個 API 請求維護與 API 服務器的連接可以大大提高性能。如果配置正確,幾乎每個 Web 服務器都應該支持保持連接。
#9. HTTP 壓縮– HTTP 壓縮既可用于響應主體(Accept-Encoding:gzip),也可用于請求主體(Content-Encoding:gzip),以提高 HTTP API 的網絡性能。
#10. HTTP 緩存– 在您的 API 響應上提供 Cache-Control 標頭。如果它們不可緩存,“Cache-Control: no-cache”將確保代理和瀏覽器理解這一點。如果它們是可緩存的,則需要考慮多種因素,例如緩存是否可以由代理共享,或者資源的“新鮮度”有多長。(RFC2616 §14.9)
#11. 緩存驗證– 如果您有可緩存的 API 命中,則應在響應中提供 Last-Modified 或 ETag 標頭,然后支持條件請求的 If-Modified-Since 或 If-None-Match 請求標頭。這將允許客戶端檢查其緩存副本是否仍然有效,并防止在不需要時下載完整的資源。如果實施得當,您可以使條件請求比普通請求更高效,還可以節(jié)省一些服務器端負載。(RFC2616 §13.3)
#12. 條件修改– ETag 標頭還可用于啟用對資源的條件修改。通過在 GET 上提供 ETag 標頭,后續(xù)的 POST、PATCH 或 DELETE 請求可以提供 If-Match 標頭來檢查它們是否在以上次看到的相同狀態(tài)更新或刪除資源。(RFC2616 §14.24)
#13. 絕對重定向– 這是 HTTP/1.1 鮮為人知的要求,即重定向(例如 201、301、302、303、307 響應代碼)應該在 Location 響應標頭中包含絕對 URI。許多客戶端確實支持 Location 中的相對 URI,但如果您希望 API 與許多客戶端廣泛兼容,則應在任何重定向中使用絕對 URI。(RFC2616 §14.30)
#14. 鏈接響應標頭– 在 RESTful API 中,即使響應的內容類型沒有提供鏈接的自然方式(例如,PDF 或圖像表示),也經常需要提供指向其他資源的鏈接。RFC5988指定了一種在響應標頭中提供鏈接的方法。
#15. 規(guī)范 URL – 對于具有多個 URL 的資源,RFC6596定義了提供規(guī)范 URL 鏈接的一致方法。
#16. 分塊傳輸編碼– 如果您有大量內容響應,傳輸編碼:分塊是將響應流式傳輸到客戶端的好方法。它將減少服務器和中間服務器的內存使用要求(尤其是實施 HTTP 壓縮時),并提供更快的首字節(jié)響應時間。
#17. 分塊傳輸編碼中的錯誤處理– 在實施分塊傳輸編碼之前,先弄清楚如何處理請求中發(fā)生的錯誤。一旦開始流式傳輸響應,就無法更改 HTTP 狀態(tài)代碼。通常,您會在內容類型中定義一種表示錯誤的方法。
#18. X-HTTP-Method-Override – 一些 HTTP 客戶端只能支持 GET 和 POST;您可以通過 POST 隧道傳輸其他 HTTP 方法,并使用事實上的標準 X-HTTP-Method-Override 標頭來記錄“真正的”HTTP 方法。
#19。URL 長度– 如果您的 API 支持復雜或任意過濾選項作為 GET 參數,請記住,如果 URL 長度超過 2000 個字符,客戶端和服務器都可能存在兼容性問題。
#20. 無狀態(tài)– 有一個地方你不希望你的 API 存儲狀態(tài),那就是你的應用服務器。始終保持應用服務器無狀態(tài),這樣它們才能輕松無痛地擴展。
#21. 內容協商——如果您想要支持資源的多種表現形式,則可以使用內容協商(例如,Accept 標頭),或針對不同表現形式使用不同的 URL(例如,…?format=json),或者您可以將兩者結合起來,讓您的內容協商資源重定向到特定格式。
#22. URI 模板– URI 模板是一種定義明確的機制,用于為您的客戶提供 URL 組合功能,或為您的最終用戶記錄您的 URL 訪問模式。
#23. 為意圖而設計——不要僅僅通過 API 公開內部業(yè)務對象。設計 API 時應使其具有語義含義,并與用戶的使用情況相匹配。Darrel Miller在 API Craft 上發(fā)表了一篇很棒的文章,對此的描述比我更好。(編輯:我在一篇題為“停止設計脆弱的 Web API”的文章中盡了最大努力。)
#24. 版本控制– 理論上,如果您預先設計了一個非常棒的 AP??I,您可能永遠不需要在 API 中創(chuàng)建不兼容性。對于我們這些實用主義者來說,請在 API URL 中添加版本控制(例如 /v1/ 路徑),這樣當 API 無法按預期工作時,您就有了安全網。(編輯:一個擴展的理由是我的后續(xù)文章:沒人有時間做這件事:API 版本控制)
#25. 授權– 在設計 API 時,請記住并非所有用戶都有權訪問系統中的所有對象。如果您使用或構建具有某種聲明性安全性的 API 框架,那就太好了,這樣就可以輕松分配和修改對資源的讀寫訪問的授權。
#26. 批量操作– 如果大多數客戶端能夠發(fā)出更少的請求來獲取或修改更多數據,那么它們的性能會更好。將批量操作構建到 API 中以支持此類用例是一個好主意。
#27. 分頁– 分頁在 API 中有兩個主要用途:減少傳遞給客戶端的不必要數據量,并減少應用服務器上的不必要計算量。制作分頁集合資源有許多不同的模式;如果您不知道從哪里開始,請瀏覽 Stackoverflow 獲取一些提示。如果您想成為我的個人英雄,請通過提供帶有時間戳或版本的其他頁面的鏈接來實現一致的分頁,這樣即使涉及的對象發(fā)生變化,您也不會在分頁請求中看到重復的結果。
#28. Unicode – 如今,很明顯您需要在 Web 服務中支持多種字符,而不僅僅是英語字符;在設計和測試 API 時請記住這一點。特別是,如果您將 Unicode 字符用作 URL 中的自然鍵(例如,/users/jimbob/ 變?yōu)?/users/??/),它們可能會很有趣。
#29. 錯誤日志記錄– 確保你設計了 API 執(zhí)行錯誤日志記錄的方式,而不是隨便拼湊起來。特別是,我發(fā)現區(qū)分由客戶端輸入引起的錯誤和由你的軟件引起的錯誤非常有價值。將它們保存在兩個單獨的日志中。
#30. 內容類型– 整本書都可以寫關于內容類型的內容;我要指出的是,內容類型非常重要。就我個人而言,我喜歡重復使用其他人開發(fā)的內容類型,例如Atom、Collection+JSON、JSON HAL或 XHTML。定義自己的內容類型比您想象的要費力得多。
#31. HATEOAS – 超媒體作為應用程序狀態(tài)的引擎是一種 REST 約束,簡單來說,就是您的內容應該通過鏈接和表單告訴客戶端下一步可以做什么。如果您在構建 API 時考慮到這一約束,那么它將更能適應變化……如果您的客戶端也遵循您的設計方法。
#32. 日期/時間– 當您在 API 中提供日期/時間值時,請使用包含時區(qū)信息的格式。RFC3339是ISO 8601 的一個子集,是最簡單的日期和時間格式。
#33. SSL – 考慮是否應該在 HTTP 和 HTTPS 下提供 API,還是只提供 HTTPS。只提供 HTTPS 是一種越來越流行的選擇。
#34. 跨站點請求偽造 (CSRF) – 如果您的 API 接受與交互式用戶相同的身份驗證配置,那么您可能容易受到 CSRF 攻擊。例如,如果您的交互式用戶登錄并獲取“SESSIONID”cookie,并且該 cookie 也可用于調用 API 請求,那么精心編寫的HTML 表單可能會代表您的用戶發(fā)出意外的 API 請求。(編輯:閱讀更多有關保護 API 免受 CSRF 攻擊的信息)
#35. 限制– 確保某個 API 用戶不會通過編寫非常愚蠢的 API 客戶端來破壞您的系統。這種情況會發(fā)生,無論是意外還是惡意。如果 API 用戶超出了您應為其提供的寬松 API 請求限制,請向他們提供帶有Retry-After 標頭的503 響應。
#36. 微妙的拒絕服務– 限制應該可以以最簡單的方式阻止某人破壞您的 API,但也存在許多微妙的拒絕服務攻擊。Slowloris、Billion laughs和ReDoS是有趣的 DoS 攻擊示例,這些攻擊不需要大量源資源,但它們會使您的 API 很快耗盡資源。
無論您向用戶提供測試代碼還是為他們構建 SDK,請檢??查您提供給他們的客戶端是否遵循一些簡單的規(guī)則:
#37. 連接保持活動– 一些 HTTP 客戶端庫要求您做一些額外的工作來啟用持久連接。持久連接可能會對 API 的感知性能產生重大影響。
#38. 授權前 401 – HTTP 客戶端的另一個怪癖是,它們通常需要“401 未授權”響應,然后才會發(fā)出帶有授權標頭的請求。這會讓您的 API 請求耗費大量時間,尤其是在高延遲難以應對的移動網絡上。
#39. 期望:100-繼續(xù)– 我知道至少有一個 API 客戶端默認使用“期望:100-繼續(xù)”;如果它沒有收到“100 繼續(xù)”響應,它將在 3 秒超時后繼續(xù)請求。如果您不支持“100 繼續(xù)”,這將是另一個可以在客戶端禁用的性能拖累。
#40. 文檔– 編寫 API 文檔可能非常無聊,但手寫文檔通常是最好的文檔。務必包含一些可運行的代碼或 curl 命令行,以幫助人們盡快掌握。您還可以查看文檔工具,例如apiary.io、Mashery I/O Docs或Swagger。
#41. 與客戶一起設計! – 不要在真空中設計 API;與客戶及其用例合作。這將幫助您“為意圖而設計”(#23)。
#42. 反饋– 確保為 API 用戶提供一種反饋 API 的方法。這可以通過您的支持渠道、托管論壇或郵件列表來實現。盡可能讓您的用戶獲得無摩擦反饋。
#43. 自動化測試– 您的 API 應該是您構建自動化測試最簡單的東西。畢竟,它是為自動化而設計的。充分利用它吧!
Microsoft Azure REST API Guidelines