
如何快速實現REST API集成以優化業務流程
/users/:org/repos
/repos/:owner/:repo
/repos/:owner/:repo/tags
/repos/:owner/:repo/branches/:branch
我們可以看到幾個特性:
最常見的一種設計錯誤,就是URL包含動詞。 因為”資源”表示一種實體,所以應該是名詞,URL 不應該有動詞,動詞應該放在 HTTP Method (參考下一條)中。舉例來說,某個 URL 是 /users/show/1
,其中 show
是動詞,這個 URL 就設計錯了,正確的寫法應該是 /users/1
,然后用 HTTP GET 方法表示 show。
如果某些動作是HTTP 動詞表示不了的,你可以把動作看成是一種資源。比如網上匯款,從賬戶1向賬戶2匯款500元,錯誤的 URL 是:
POST /accounts/1/transfer/500/to/2
正確的寫法是把動詞 transfer 改成transaction,資源不能是動詞,但是可以是一種服務:
POST /transactoin HTTP/1.1
HOST: 127.0.0.1
from=1&to=2&amount=500
有了資源的 URI 設計,所有針對資源的操作都是使用 HTTP 方法指定的。比較常用的 HTTP/1.1 動詞有下面5個:
還有4個不常用的 HTTP/1.1 動詞:
舉例:
GET /repos/:owner/:repo/issues
GET /repos/:owner/:repo/issues/:number
POST /repos/:owner/:repo/issues
PATCH /repos/:owner/:repo/issues/:number
DELETE /repos/:owner/:repo
這里順帶探討一下,HTTP 協議涉及到的一種重要性質:冪等性(Idempotence)。在 HTTP/1.1 規范中冪等性的定義是:
Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.
從定義上看,HTTP 方法的冪等性是指一次和多次請求某一個資源應該具有同樣的副作用。冪等性屬于語義范疇,正如編譯器只能幫助檢查語法錯誤一樣,HTTP 規范也沒有辦法通過消息格式等語法手段來定義它,這可能是它不太受到重視的原因之一。但實際上,冪等性是分布式系統設計中十分重要的概念,而 HTTP 的分布式本質也決定了它在 HTTP 中具有重要地位。
安全方法是指不修改資源的 HTTP 方法。譬如,當使用 GET 或者 HEAD 作為資源 URL,都必須不去改變資源。然而,這并不全準確。意思是:它不改變資源的表示形式。對于安全方法,它仍然可能改變服務器上的內容或資源,但這必須不導致不同的表現形式。
HTTP Method | 冪等 | 安全 | |
---|---|---|---|
1 | OPTIONS | yes | yes |
2 | GET | yes | yes |
3 | HEAD | yes | yes |
4 | PUT | yes | no |
5 | POST | no | no |
6 | DELETE | yes | no |
7 | PATCH | no | no |
實際上接口的冪等或者安全與否取決于接口的實現,只是 HTTP Method 語義上我們約定俗成地認為實現的過程會參照上表所示。對于冪等的接口,客戶端就可以放心地多次調用,網關層面也可以重試。
HTTP 應答中,需要帶一個很重要的字段:status code
。它說明了請求的大致情況,是否正常完成、需要進一步處理、出現了什么錯誤,對于客戶端非常重要。狀態碼都是三位的整數,大概分成了幾個區間:
2XX
:請求正常處理并返回3XX
:重定向,請求的資源位置發生變化4XX
:客戶端發送的請求有錯誤5XX
:服務器端錯誤在 HTTP API 設計中,經常用到的狀態碼以及它們的意義如下表:
Status Code | 語義 | 說明 |
---|---|---|
200 | OK | 請求已成功 |
201 | Created | 請求已完成,并導致了一個或者多個資源被創建,最常用在 POST 創建資源的時候 |
202 | Accepted | 請求已經接收并開始處理,但是處理還沒有完成。一般用在異步處理的情況,響應 body 中應該告訴客戶端去哪里查看任務的狀態 |
204 | No Content | 請求已經處理完成,但是沒有信息要返回,經常用在 PUT 更新資源的時候(客戶端提供資源的所有屬性,因此不需要服務端返回)。如果有重要的 metadata,可以放到頭部返回 |
301 | Moved Permanently | 請求的資源已經永久性地移動到另外一個地方,后續所有的請求都應該直接訪問新地址。服務端會把新地址寫在 Location 頭部字段,方便客戶端使用。允許客戶端把 POST 請求修改為 GET。 |
302 | Moved Temporarily | 臨時重定向 |
304 | Not Modified | 請求的資源和之前的版本一樣,沒有發生改變。用來緩存資源,和條件性請求(conditional request)一起出現 |
307 | Temporary Redirect | 目標資源暫時性地移動到新的地址,客戶端需要去新地址進行操作,但是 不能 修改請求的方法。 |
308 | Permanent Redirect | 和 301 類似,除了客戶端 不能 修改原請求的方法 |
400 | Bad Request | 1.語義有誤,當前請求無法被服務器理解; 2. 請求參數有誤。 |
401 | Unauthorized | 當前請求需要身份驗證。 |
403 | Forbidden | 服務器已經理解請求,但是拒絕執行它。與401響應不同的是,身份驗證并不能提供任何幫助,而且這個請求也不應該被重復提交。如果這不是一個 HEAD 請求,而且服務器希望能夠講清楚為何請求不能被執行,那么就應該在實體內描述拒絕的原因。當然服務器也可以返回一個404響應,假如它不希望讓客戶端獲得任何信息。 |
404 | Not Found | 請求失敗,請求所希望得到的資源未被在服務器上發現。 |
405 | Method Not Allowed | 請求行中指定的請求方法不能被用于請求相應的資源。該響應必須返回一個Allow 頭信息用以表示出當前資源能夠接受的請求方法的列表。鑒于 PUT,DELETE 方法會對服務器上的資源進行寫操作,因而絕大部分的網頁服務器都不支持或者在默認配置下不允許上述請求方法,對于此類請求均會返回405錯誤。 |
406 | Not Acceptable | 請求的資源的內容特性無法滿足請求頭中的條件,因而無法生成響應實體。 |
409 | Conflict | 由于和被請求的資源的當前狀態之間存在沖突,請求無法完成。 |
429 | Too Many Requests | 資源配額不足或達到速率限制。 |
499 | Client Closed Request | 請求被客戶端取消。 |
500 | Internal Server Error | 服務器內部錯誤 |
501 | Not Implemented | 服務器不支持當前請求所需要的某個功能。當服務器無法識別請求的方法,并且無法支持其對任何資源的請求。 |
502 | Bad Gateway | 作為網關或者代理工作的服務器嘗試執行請求時,從上游服務器接收到無效的響應。 |
503 | Service Unavailable | 由于臨時的服務器維護或者過載,服務器當前無法處理請求。這個狀況是臨時的,并且將在一段時間以后恢復。 |
504 | Gateway Timeout | 作為網關或者代理工作的服務器嘗試執行請求時,未能及時從上游服務器(URI標識出的服務器,例如HTTP、FTP、LDAP)或者輔助服務器(例如DNS)收到響應。 |
505 | HTTP Version Not Supported | 服務器不支持,或者拒絕支持在請求中使用的 HTTP 版本。 |
上面這些狀態碼覆蓋了 API 設計中大部分的情況,如果對某個狀態碼不清楚或者希望查看更完整的列表,可以參考?HTTP Status Code[6]?、維基百科-狀態碼[7]或者?RFC7231 Response Status Codes[8]?的內容。
如果出錯,應該在 response body 中通過?message
?給出明確的錯誤信息(一般來說,返回的信息中將 message 作為鍵名,出錯詳情作為鍵值即可)。比如客戶端發送的請求有錯誤,一般會返回?4XX Bad Request
?結果。這個結果很模糊,給出錯誤?message
?的話,能更好地讓客戶端知道具體哪里有問題,進行快速修改。
{
"message":"錯誤詳情"
}
錯誤詳情應該可以幫助用戶輕松快捷地理解和解決API 錯誤。通常,在編寫錯誤詳情時請考慮以下準則:
為了能夠長時間在眾多 API 中為開發者提供一致的體驗,API 使用的所有名稱都應該具有以下特點:
這包括接口、資源、集合、方法和消息的名稱。
由于很多開發者不是以英語為母語,所以這些命名慣例的目標之一是確保大多數開發者可以輕松理解 API。對于方法和資源,我們鼓勵使用簡單、直觀和少量的詞匯來命名。
一般來說,讓任何人隨意訪問公開的 API 是不好的做法。驗證和授權是兩件事情:
如果沒有通過驗證(提供的用戶名和密碼不匹配,token 不正確等),需要返回?401 Unauthorized[9]狀態碼,并在 body 中說明具體的錯誤信息;而沒有被授權訪問的資源操作,需要返回?403 Forbidden[10]?狀態碼,還有詳細的錯誤信息。
NOTES: 借鑒于 Github,它對某些用戶未被授權訪問的資源操作返回 404 Not Found[11],目的是為了防止私有資源的泄露(比如黑客可以自動化試探用戶的私有資源,返回 403 的話,就等于告訴黑客用戶有這些私有的資源)。
如果對訪問的次數不加控制,很可能會造成 API 被濫用,甚至被 DDOS 攻擊[12]。根據使用者不同的身份對其進行限流,可以防止這些情況,減少服務器的壓力。
對用戶的請求限流之后,要有方法告訴用戶它的請求使用情況,本文檔推薦使用的三個相關的頭部:
X-RateLimit-Limit
: 用戶每個小時允許發送請求的最大值X-RateLimit-Remaining
:當前時間窗口剩下的可用請求數目X-RateLimit-Rest
: 時間窗口重置的時候,到這個時間點可用的請求數量就會變成 X-RateLimit-Limit
的值舉例:
X-Ratelimit-Limit: 18000
X-Ratelimit-Remaining: 17995
X-Ratelimit-Reset: 1590570990
如果允許沒有登錄的用戶使用 API(可以讓用戶試用),可以把 X-RateLimit-Limit
的值設置得很小,比如 60
。沒有登錄的用戶是按照請求的 IP 來確定的,而登錄的用戶按照認證后的信息來確定身份。
對于超過流量的請求,可以返回 429 Too many requests[13] 狀態碼,并附帶錯誤信息。
API 最終是給人使用的,不管是公司內部,還是公開的 API 都是一樣。即使我們遵循了上面提到的所有規范,設計的 API 非常優雅,用戶還是不知道怎么使用我們的 API。最后一步,但非常重要的一步是:為你的 API 編寫優秀的文檔。
對每個請求以及返回的參數給出說明,最好給出一個詳細而完整地示例,提醒用戶需要注意的地方……反正目標就是用戶可以根據你的文檔就能直接使用 API,而不是要發郵件給你,或者跑到你的座位上問你一堆問題。
429 Too many requests:?https://httpstatuses.com/429[1]
OpenAPI Specfication: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md[2]
Google API Design Guide: https://cloud.google.com/apis/design[3]
阿里云: https://bytedance.feishu.cn/docs/doccnrP6Ud3YTavonOlxds9lzad#I1tZbK[4]
Github API Design: https://developer.github.com/v3/#current-version[5]
Versioning REST Services: https://www.informit.com/articles/article.aspx?p=1566460[6]
HTTP Status Code: https://httpstatuses.com/[7]
維基百科-狀態碼: https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81[8]
RFC7231 Response Status Codes: https://tools.ietf.org/html/rfc7231#section-6[9]
401 Unauthorized: https://httpstatuses.com/401[10]
403 Forbidden: https://httpstatuses.com/403[11]
404 Not Found: https://httpstatuses.com/404[12]
DDOS 攻擊: https://en.wikipedia.org/wiki/Denial-of-service_attack[13]
文章轉自微信公眾號@朱小廝的博客