總方針
構建易于理解和使用的RESTful
接口晒屎。
接口應該是直觀的淫半,調用者可以通過接口來獲得系統(tǒng)或應用程序中所有業(yè)務服務的工作節(jié)奏逆济。
一般來說榛斯,可以使用以下的指導方針來進行接口的設計蔓钟。
- 使用標準
HTTP
動詞--圍繞這些HTTP
動詞(GET
/PUT
/POST
/PATCH
和DELETE
)對基本的行為進行建模永票。 - 使用
URI
來傳達意圖--使用URI
來描述問題域中的不同資源,并為問題域內的資源的關系提供一種基本機制滥沫。 - 使用
JSON
進行響應--JSON
是一種輕量級的數(shù)據(jù)序列化協(xié)議侣集。 - 使用
HTTP
狀態(tài)碼來傳達結果--HTTP
協(xié)議具有豐富的標準響應代碼,來指示服務的成功和失敗兰绣。學習這些狀態(tài)碼世分,并且,最重要的是缀辩,在所有接口中始終如一地使用它們臭埋。
所有這些指導方針都是為了完成一件事,那就是使接口易于理解和使用臀玄。
我們希望調用者坐下來查看一下接口就能開始使用它們瓢阴。
如果接口不容易使用,開發(fā)人員就會另辟道路健无,破壞架構的意圖荣恐。
資源與URI
REST
全稱是 Representational State Transfer (表述性狀態(tài)轉移)。其中表述指的就是資源累贤。
URI
既可看成是資源的地址叠穆,也可以看成是資源的名稱。
URI
的設計應該遵循可尋址性原則臼膏,具有自描述性硼被,需要在形式上給人以直覺的關聯(lián)。
URI
是HTTP
動詞作用的對象讶请。它應該只有名詞祷嘶,不能包含動詞屎媳。
URI
的設計應該注意:
-
URI
中不能有動詞: 動詞應該由HTTP
的動作(GET
/POST
/PUT
/PATCH
/DELETE
等)來表示 -
URI
結尾不應該包含斜杠“/” - 正斜杠分隔符“/”必須用來指示層級關系
- 應該使用連接符“-”來提高
URI
的可讀性:瀏覽器默認會給超鏈接加上下劃線夺溢,因此不要用其做URI
分隔符 -
URI
路徑首選小寫字母:RFC-3986
將URI
定義為區(qū)分大小寫,但URI
中的scheme
(協(xié)議名)和host
(主機名)除外 -
URI
路徑中的名詞建議使用復數(shù) - 避免層級過深的
URI
(太多的層級在另一個側面反應該接口有太多的職責)
資源操作
HTTP
通常有以下5種動詞:
-
GET
:獲取資源(冪等) -
POST
:新建資源(非冪等) -
PUT
:更新資源(所有屬性)(冪等) -
PATCH
:更新資源(部分屬性)(非冪等) -
DELETE
:刪除資源(冪等)
根據(jù) HTTP
規(guī)范烛谊,動詞一律大寫风响。
資源過濾
很多情況,資源會有多級分類丹禀,因此很容易寫出多級的URI
状勤,比如某個作者的某一類文章(/authors/123/categories/2
)鞋怀。
這種URI
不易于擴展,語義也不明確持搜,不能直觀表達其含義密似。
更好的做法是,將次要的級別用查詢字符串進行表達葫盼。如:
/authors/123?category=2
/articles?published=true
同樣的残腌,通過使用查詢字符串,實現(xiàn)排序贫导、投影和分頁抛猫。
與之相反
經常使用的、復雜的查詢可以標簽化孩灯。
如:
/authors/123?status=close&sort=created,desc
可轉化為:
/authors/123/closed
// 或者
/authors/123#closed
返回狀態(tài)碼
HTTP
狀態(tài)碼為三位數(shù)闺金,分五類:
-
1**
相關信息 -
2**
操作成功 -
3**
重定向相關 -
4**
客戶端(導致的)錯誤 -
5**
服務端(導致的)錯誤
HTTP
包含了100多個狀態(tài)碼,覆蓋了大多數(shù)可能遇到的情況峰档。
每一種狀態(tài)都有標準的(或約定的)解釋败匹,客戶端只需查看狀態(tài),就可以大致判斷發(fā)生了什么情況面哥。
所以服務器應該盡可能使用這些標準的HTTP
狀態(tài)碼哎壳,來表達執(zhí)行結果狀態(tài)。
通常不需要1**
這一類狀態(tài)碼尚卫。
以下是常用的:
-
200 OK
: 成功返回請求數(shù)據(jù)(冪等) -
201 Created
: 新建數(shù)據(jù)成功 -
202 Accepted
: 表示服務器已接收請求归榕,但未處理。通常用于異步操作吱涉。 -
204 No Content
: 刪除數(shù)據(jù)成功 -
301 Moved Permanently
: 資源已永久性遷移刹泄,需要使用新的(寫在相應頭Location
中的)URI
訪問。允許客戶端把POST
請求修改為GET
怎爵。 -
302 Found
: 不推薦使用特石,此代碼在HTTP1.1
協(xié)議中已被303
/307
替代。目前對302
的使用和最初的HTTP1.0
定義的語義是有出入的鳖链,應該只有在GET
/HEAD
方法下姆蘸,客戶端才能根據(jù)Location
執(zhí)行自動跳轉,而目前的客戶端基本上是不會判斷原請求方法芙委,無條件的執(zhí)行重定向逞敷。 -
303 See Other
: 參考另一個URI
(區(qū)別:307
用于GET
;303
用于POST
灌侣、PUT
和DELETE
)推捐,但不強制要求重定向。 -
304 Not Modified
: 服務器資源與客戶端最近訪問的一致侧啼,不返回資源消息體牛柒。 -
307 Temporary Redirect
: 目前URI
不能提供所請求的資源堪簿,臨時重定向到另外一個URI
。用來替代HTTP1.0
中的302
-
308 Permanent Redirect
: 與301
類似皮壁,但客戶端不能修改原請求方法 -
400 Bad Request
: 服務器不理解客戶端的請求椭更,未做任何處理 -
401 Unauthorized
: 用戶未提供身份驗證憑據(jù),或沒通過驗證 -
403 Forbidden
: 用戶通過的驗證蛾魄,但不具有訪問權限 -
404 Not Found
: 請求資源不存在或不可用甜孤。可以對某些用戶未授權訪問的資源操作返回該狀態(tài)碼畏腕,目的是防止私有資源泄露(知道有該資源)缴川。 -
405 Method Not Allowed
: 用戶已通過驗證,但所用的HTTP
方法不在權限內或資源只讀等描馅。響應Header
中應申明支持的方法 -
406 Not Acceptable
: 表示拒絕處理該請求(如:服務端只能返回JSON
把夸,但客戶端要求XML
) -
409 Conflict
: 資源狀態(tài)沖突,例如客戶端嘗試刪除一個有約束的資源 -
410 Gone
: 請求資源已從這個地址轉移铭污,不再可用 -
412 Precondition Failed
: 用于有條件的操作不能被滿足 -
415 Unsupported Media Type
: 請求格式不支持(如:服務端只能返回JSON
恋日,但客戶端要求XML
) -
422 Unprocessable Entity
: 請求無法處理,或發(fā)生了一個驗證錯誤 -
429 Too Many Requests
: 請求次數(shù)超過限制 -
500 Internal Server Error
: 請求有效嘹狞,服務器處理時發(fā)生內部錯誤 -
503 Service Unavailable
: 服務器無法處理請求岂膳,多半是服務器問題,如CPU高等
返回內容
返回內容數(shù)據(jù)格式應該是結構化的(如:一個JSON
對象)磅网。
客戶端請求時也要明確告訴服務器谈截,可以接受的格式。
-
GET /collections
200 返回資源列表 -
GET /collections/:id
200 返回單個資源 -
POST /colections
201 返回新增的資源對象 -
PUT /collections/:id
200 返回完整的資源對象 -
PATCH /collections/:id
200 返回完整的資源對象或被修改的屬性 -
DELETE /collections/:id
204 返回空文檔
錯誤處理
錯誤時不要返回200
狀態(tài)碼涧偷。
因為只有解析數(shù)據(jù)體后簸喂,才能得知操作失敗。而且與HTTP
狀態(tài)碼描述沖突燎潮。
假如你不利用HTTP
狀態(tài)碼豐富的應用語義喻鳄,那么你就錯失了提高重用性、增強互操作性和提升松耦合性的機會确封。
這些所謂的RESTful
應用必須通過響應體才能給出錯誤信息除呵,那么這個跟SOAP
沒什么區(qū)別。
正確的做法是爪喘,狀態(tài)碼反映發(fā)生的錯誤颜曾,而具體的錯誤信息放在數(shù)據(jù)體中。
如:
HTTP/1.1 400 Bab Request
Content-Type: application/json
{
"error": "Invalid param."
"data": {
"name": "This field is required."
}
}
另外建議要區(qū)分業(yè)務異常和非業(yè)務異常腥放。
業(yè)務異常的返回4**
的狀態(tài)碼泛啸,非業(yè)務異常的返回500
狀態(tài)碼绿语。
資源的表述
客戶端通過HTTP
方法獲取的不是資源本身秃症,而是資源的一種表述而已候址。
資源在外界的具體呈現(xiàn),可以有多種表述形式种柑,如:html岗仑、xml、json聚请、png荠雕、jpg等。
資源的表述包括數(shù)據(jù)和描述數(shù)據(jù)的元數(shù)據(jù)驶赏,如:HTTP
頭中的Content-Type
就是一個元數(shù)據(jù)屬性炸卑。
所以應該通過HTTP
的內容協(xié)商,來獲取資源的表述煤傍。
如:客戶端可以通過Accept
頭請求一種特定格式的表述盖文,服務器則通過Content-Type
告訴客戶端資源的表述形式。
區(qū)分格式
應該優(yōu)先使用內容協(xié)商來區(qū)分表述格式蚯姆。
使用后綴表示格式五续,無疑是直觀的,但它混淆了資源的名稱和資源的表述形式龄恋。
超媒體(Hypermedia)
“超媒體即應用狀態(tài)引擎(hypermedia as the engine of application state)”疙驾。
當瀏覽Web網(wǎng)頁時,我們從一個鏈接跳到一個頁面郭毕,再從頁面里的另一個鏈接跳到另一個頁面它碎,這就是在用超媒體的概念:把一個個資源鏈接起來。
要達到這個目的显押,就要求在資源的表述里加上鏈接來引導客戶端链韭。
如 GitHub api 中的分頁,是在頭信息的Link
提供:
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=15>; rel="next",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=1>; rel="first",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=13>; rel="prev"
應該多花時間來給資源的表述提供鏈接煮落,而不是專注于尋找漂亮的URI
敞峭。
速率限制
響應頭建議包含當前限流狀態(tài)
如 GitHub api 中使用3個相關的頭信息進行說明:
-
X-RateLimit-Limit
: 用戶在時間窗口下發(fā)送請求的最大值 -
X-RateLimit-Remaining
: 當前時間窗口剩下的可用請求數(shù) -
X-RateLimit-Rest
: 為了得到最大請求數(shù)(或到下一時間窗口)所等待的秒數(shù)
建議同時提供可以不影響RateLimit
的請求接口,來查詢當前的RateLimit
蝉仇。
無狀態(tài)
RESTful
應該是無狀態(tài)通信的旋讹。
服務端不應該保存客戶端(應用)狀態(tài)。
客戶端與服務端交互必須是無狀態(tài)的轿衔,并在每次請求中包含處理所需的一切信息沉迹。
這種無狀態(tài)通信,使得服務端能夠理解獨立的請求和響應害驹。
在多次請求中鞭呕,同一客戶端也不再需要依賴同一服務器,方便實現(xiàn)高可擴展和高可用性的服務端宛官。
服務端通過超媒體告訴客戶端當前(應用)狀態(tài)可以有哪些后續(xù)的狀態(tài)葫松。
這些類似“下一頁”的鏈接將指引客戶端如何從當前狀態(tài)進入下一個可能的狀態(tài)瓦糕。
版本
三種方式:
-
URI
中:/api/v1/**
-
Accept Header
:Accept: application/json+v1
- 自定義
Header
:X-Api-Version: 1
建議第一種,雖然沒那么優(yōu)雅腋么,但最明顯方便咕娄。
另一種觀點:一個資源,應只有一個單一的URI
來標示珊擂,資源版本不應該體現(xiàn)在URI
中圣勒。
以上見仁見智,不強制要求摧扇。
- API 失效:返回
404 Not Found
或410 Gone
- API 遷移:返回
301
/303
或307
其他(Header)頭信息的使用
-
Last-Modified
: 用于服務器端的響應圣贸,是一個資源最后被修改的時間戳,客戶端(緩存)可以根據(jù)此信息判斷是否需要重新獲取該資源扛稽。 -
ETag
: 服務器端資源版本的標識旁趟,客戶端(緩存)可以根據(jù)此信息判斷是否需要重新獲取該資源,需要注意的是庇绽,ETage
如果通過服務器隨機生產锡搜,可能會存在多個主機對同一個資源產生不同的ETag
的問題。 -
Location
: 如在成功創(chuàng)建了一個資源后瞧掺,可以把新資源的URL
放在Location
中耕餐;又如,在異步請求時辟狈,接口返回響應202(Accepted)
的同時肠缔,可以給客戶端一個查詢異步狀態(tài)的地址。 -
Cache-Control
,Expires
,Date
: 通過緩存機制提升接口響應性能哼转,同時根據(jù)實際需要也可以禁止客戶端對接口請求做緩存明未。對應某些實時性要求不高的情況下,可以使用max-age
來指定一個小的緩存時間壹蔓,這樣對客戶端和服務端都有利趟妥。一般來說,只對GET
方法且返回200
的資源使用緩存佣蓉,在某些情況下也可以對返回3**
或4**
的情況做緩存披摄,防范錯誤訪問帶來的負載。 - 自定義頭 : 不能改變
HTTP
方法的性質勇凭,盡量保持的簡單疚膊,不要用body
中的信息對其補充說明。
其他
1. 動詞的覆蓋
有些客戶端僅支持GET
和POST
兩種方法虾标。那么寓盗,服務器必須可以接受通過POST
模擬其他方法(PUT
、PACTH
、DELETE
)傀蚌。
客戶端在發(fā)送HTTP
請求時基显,要加上X-HTTP-Method-Override
頭信息,告訴服務器應該使用那個動詞喳张,覆蓋POST
方法。
2. 提供相關鏈接
服務接口的使用者未必知道接口有那些,以及它的相關服務。
好的接口钻哩,應該在相應中給出相關鏈接寡润,以便于下一步操作。
這樣蚜印,用戶就可以發(fā)現(xiàn)其他接口的URI
。
這種方法叫HATEOAS
。
如 GitHub 的 API 都在api.github.com這個域名擂涛。