要說RESTful首先來說說REST – REpresentational State Transfer (表述性狀態(tài)傳遞)
表述性狀態(tài)轉移是一組架構約束條件和原則强岸。滿足這些約束條件和原則的應用程序或設計就是RESTful。需要注意的是卵洗,REST是設計風格而不是標準请唱。
以上的概念大概是許多關于RESTful中都會出現的定義概念弥咪。
那么什么是表述性狀態(tài)轉移呢?
首先十绑,之所以晦澀是因為前面主語被去掉了聚至,全稱是 Resource Representational State Transfer,通俗來講就是本橙,資源在網絡中以某種表現形式進行狀態(tài)轉移扳躬。
在查詢很多資料后看到一句很精簡的總結:
URL定位資源,用HTTP動詞(GET,POST,DELETE,PUT等)描述操作甚亭。
既然說到了是用HTTP動詞進行操作贷币。那么需要了解這里列出的4.5個非常重要的HTTP動作,這里的0.5個是指PATCH亏狰,因為它在功能上與PUT非常類似役纹,剩下4個通常被API開發(fā)人員兩兩結合使用
GET(SELECT):從服務器獲取一個指定資源或一個資源集合;
POST(CREATE):在服務器上創(chuàng)建一個資源暇唾;
PUT(UPDATE):更新服務器上的一個資源促脉,需要提供整個資源;
PATCH(UPDATE):更新服務器上的一個資源策州,只提供資源中改變的那部分屬性瘸味;
DELETE(DELETE):移除服務器上的一個資源;
還有兩個不常見的HTTP動作:
HEAD – 獲取一個資源的元數據够挂,例如一組hash數據或者資源的最近一次更新時間旁仿;
OPTIONS – 獲取當前用戶(Consumer)對資源的訪問權限;
關于RESTful的API設計風格孽糖,說完RESTful接下來該說說API了枯冈。
API是服務提供方和使用方之間的契約,打破該契約將會給服務端開發(fā)人員招來非常大的麻煩梭姓,這些麻煩來自于使用API的開發(fā)人員霜幼,因為對API的改動會導致他們的移動app無法工作。一個好的文檔對于解決這些事情能起到事半功倍的作用誉尖,但是絕對多數程序員都不喜歡寫文檔罪既。如果想讓服務端的價值更好的體現出來,就要好好設計API铡恕。通過這些API琢感,你的服務/核心程序將有可能成為其他項目所依賴的平臺;你提供的API越易用探熔,就會有越多人愿意使用它驹针。規(guī)劃API的展示形式可能比你想象的要簡單,首先要確定你的數據是如何設計以及核心程序是如何工作的诀艰。
image.png
也就是說Server提供的RESTful API中柬甥,URL中只使用名詞來指定資源饮六,原則上不使用動詞】疗眩“資源”是REST架構或者說整個網絡處理的核心卤橄。
那么下面來具體說說如何形成良好的RESTful風格的API設計
1. 使用名詞而不是動詞
Server提供的RESTful API中,URL中只使用名詞來指定資源臂外,原則上不使用動詞窟扑。“資源”是REST架構或者說整個網絡處理的核心漏健。比如:
http://api.qc.com/v1/newsfeed: 獲取某人的新鮮;
http://api.qc.com/v1/friends: 獲取某人關系列表;
http://api.qc.com/v1/profile: 獲取某人的詳細信息;
URL是對資源描述的抽象嚎货,資源的描述一定是名詞,如果引入了動詞蔫浆,那這個URL就表示了一個動作殖属,而非一個資源,這樣就偏離了REST的設計思想
2.Get方法和查詢參數不應該涉及狀態(tài)改變
使用PUT, POST 和DELETE 方法 而不是 GET 方法來改變狀態(tài)瓦盛,不要使用GET 進行狀態(tài)改變:
通常忱辅,GET請求能夠被瀏覽器緩存(而且通常都會這么做),例如谭溉,當用戶發(fā)起第二次POST請求時,緩存的GET請求(依賴于緩存首部)能夠加快用戶的訪問速度橡卤。一個HEAD請求基本上就是一個沒有返回體的GET請求扮念,因此也能被緩存。
3.使用復數名詞
不要混淆名詞單數和復數碧库,為了保持簡單柜与,只對所有資源使用復數。
4. 使用子資源表達關系
如果一個資源與另外一個資源有關系嵌灰,使用子資源:
5.使用Http頭聲明序列化格式
在客戶端和服務端弄匕,雙方都要知道通訊的格式,格式在HTTP-Header中指定
Content-Type 定義請求格式
Accept 定義系列可接受的響應格式
6.使用HATEOAS
Hypermedia as the Engine of Application State 超媒體作為應用狀態(tài)的引擎沽瞭,超文本鏈接可以建立更好的文本瀏覽:
7.為集合提供過濾 排序 選擇和分頁等功能
Filtering過濾:
使用唯一的查詢參數進行過濾:
GET /cars?color=red 返回紅色的cars
GET /cars?seats<=2 返回小于兩座位的cars集合
當用戶請求獲取一組對象列表時迁匠,你就需要對結果進行過濾并返回一組嚴格符合用戶要求的對象。有時返回結果的數量可能非常大驹溃,但是你也不能隨意對此進行約束城丧,因為這種服務端的隨意約束會造成第三方開發(fā)人員的困惑。如果用戶請求了一個集合豌鹤,并對返回結果進行遍歷亡哄,然后只要前100個對象,那么這里就需要由用戶來指明這個限制量布疙。這樣用戶就不會有這樣的疑惑:是他們程序的bug還是接口限制了100條蚊惯?還是網絡只允許傳這么大的包愿卸?
Sorting排序:
允許針對多個字段排序
GET /cars?sort=-manufactorer,+model
這是返回根據生產者降序和模型升序排列的car集合
Field selection
移動端能夠顯示其中一些字段,它們其實不需要一個資源的所有字段截型,給API消費者一個選擇字段的能力短绸,這會降低網絡流量,提高API可用性筋讨。
GET /cars?fields=manufacturer,model,id,color
Paging分頁
使用 limit 和offset.實現分頁淤刃,缺省limit=20 和offset=0;
GET /cars?offset=10&limit=5
為了將總數發(fā)給客戶端赶诊,使用訂制的HTTP頭: X-Total-Count.
鏈接到下一頁或上一頁可以在HTTP頭的link規(guī)定笼平,遵循Link規(guī)定:
Link:
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5; rel=”next”,
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3; rel=”last”,
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5; rel=”first”,
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5; rel=”prev”,
8.版本化你的API
也就是進行版本控制。無論你在設計什么系統(tǒng)舔痪,也不管你事先做了多么詳盡的計劃寓调,隨著時間的推移和業(yè)務的發(fā)展,你的程序總會發(fā)生變化锄码,數據關系也會發(fā)生變化夺英,資源可能會被添加或者刪除一些屬性。只要軟件還在生存期內并且還有人在用它滋捶,開發(fā)人員就得面對這些問題痛悯,對于API設計來說,尤其如此重窟。
在URL中加入版本號是一個優(yōu)秀的API設計载萌,當然還有另一個常用的解決辦法就是把版本號放在請求首部中
使得API版本變得強制性,不要發(fā)布無版本的API巡扇,使用簡單數字扭仁,避免小數點如2.5。一般在Url后面使用?v
/blog/api/v1
9. 使用Http狀態(tài)碼處理錯誤
如果你的API沒有錯誤處理是很難的厅翔,只是返回500和出錯堆棧不一定有用
Http狀態(tài)碼提供70個出錯乖坠,我們只要使用10個左右:
200 – OK – 一切正常
201 – OK – 新的資源已經成功創(chuàng)建
204 – OK – 資源已經成功擅長
304 – Not Modified – 客戶端使用緩存數據
400 – Bad Request – 請求無效,需要附加細節(jié)解釋如 “JSON無效”
401 – Unauthorized – 請求需要用戶驗證
403 – Forbidden – 服務器已經理解了請求刀闷,但是拒絕服務或這種請求的訪問是不允許的熊泵。
404 – Not found – 沒有發(fā)現該資源
422 – Unprocessable Entity – 只有服務器不能處理實體時使用,比如圖像不能被格式化甸昏,或者重要字段丟失戈次。
500 – Internal Server Error – API開發(fā)者應該避免這種錯誤。
1XX的返回碼預留給HTTP的底層使用筒扒,在你的整個職業(yè)生涯中都不會主動發(fā)送這種返回碼怯邪;
2XX的返回碼表示請求按照預期執(zhí)行并成功返回了信息。服務端要盡可能給用戶返回這種結果花墩。
3XX的返回碼表示請求重定向悬秉,大多數API都不會經常使用這種請求()澄步,但是最新的超媒體API會充分使用這些功能。
4XX的返回碼主要表示由客戶端引起的錯誤和泌,例如請求參數錯誤或者訪問一個不存在的資源村缸,這些必須為冪等操作,并且不能改變服務器的狀態(tài)(其實服務器的狀態(tài)發(fā)生了改變就意味著操作不是冪等了)武氓。
5XX的返回碼主要表示由服務器引起的錯誤梯皿,通常情況下,這些錯誤都是開發(fā)人員
使用詳細的錯誤包裝錯誤:
{"errors": [? {"userMessage":"Sorry, the requested resource does not exist","internalMessage":"No car found in the database","code":34,"more info":"http://dev.mwaysolutions.com/blog/api/v1/errors/12345"}? ]}12345678910111213141516171819
10.允許覆蓋http方法
一些代理只支持POST 和 GET方法县恕, 為了使用這些有限方法支持RESTful API东羹,需要一種辦法覆蓋http原來的方法。
使用訂制的HTTP頭 X-HTTP-Method-Override 來覆蓋POST 方法.
使用場景
版本號
在 RESTful API 中忠烛,API 接口應該盡量兼容之前的版本属提。但是,在實際業(yè)務開發(fā)場景中美尸,可能隨著業(yè)務需求的不斷迭代冤议,現有的 API 接口無法支持舊版本的適配,此時如果強制升級服務端的 API 接口將導致客戶端舊有功能出現故障师坎。實際上恕酸,Web 端是部署在服務器,因此它可以很容易為了適配服務端的新的 API 接口進行版本升級胯陋,然而像 Android 端尸疆、IOS 端、PC 端等其他客戶端是運行在用戶的機器上惶岭,因此當前產品很難做到適配新的服務端的 API 接口,從而出現功能故障犯眠,這種情況下按灶,用戶必須升級產品到最新的版本才能正常使用。
為了解決這個版本不兼容問題筐咧,在設計 RESTful API 的一種實用的做法是使用版本號鸯旁。一般情況下,我們會在 url 中保留版本號量蕊,并同時兼容多個版本铺罢。
【GET】? /v1/users/{user_id}// 版本 v1 的查詢用戶列表的 API 接口【GET】? /v2/users/{user_id}// 版本 v2 的查詢用戶列表的 API 接口
現在,我們可以不改變版本 v1 的查詢用戶列表的 API 接口的情況下残炮,新增版本 v2 的查詢用戶列表的 API 接口以滿足新的業(yè)務需求韭赘,此時,客戶端的產品的新功能將請求新的服務端的 API 接口地址势就。雖然服務端會同時兼容多個版本泉瞻,但是同時維護太多版本對于服務端而言是個不小的負擔脉漏,因為服務端要維護多套代碼。這種情況下袖牙,常見的做法不是維護所有的兼容版本侧巨,而是只維護最新的幾個兼容版本,例如維護最新的三個兼容版本鞭达。在一段時間后司忱,當絕大多數用戶升級到較新的版本后,廢棄一些使用量較少的服務端的老版本API 接口版本畴蹭,并要求使用產品的非常舊的版本的用戶強制升級坦仍。
注意的是,“不改變版本 v1 的查詢用戶列表的 API 接口”主要指的是對于客戶端的調用者而言它看起來是沒有改變撮胧。而實際上桨踪,如果業(yè)務變化太大,服務端的開發(fā)人員需要對舊版本的 API 接口使用適配器模式將請求適配到新的API 接口上芹啥。
資源路徑
RESTful API 的設計以資源為核心锻离,每一個 URI 代表一種資源。因此墓怀,URI 不能包含動詞汽纠,只能是名詞。注意的是傀履,形容詞也是可以使用的虱朵,但是盡量少用。一般來說钓账,不論資源是單個還是多個碴犬,API 的名詞要以復數進行命名。此外梆暮,命名名詞的時候服协,要使用小寫、數字及下劃線來區(qū)分多個單詞啦粹。這樣的設計是為了與 json 對象及屬性的命名方案保持一致偿荷。例如,一個查詢系統(tǒng)標簽的接口可以進行如下設計唠椭。
【GET】? /v1/tags/{tag_id}
同時跳纳,資源的路徑應該從根到子依次如下。
/{resources}/{resource_id}/{sub_resources}/{sub_resource_id}/{sub_resource_property}
我們來看一個“添加用戶的角色”的設計贪嫂,其中“用戶”是主資源寺庄,“角色”是子資源。
【POST】? /v1/users/{user_id}/roles/{role_id}// 添加用戶的角色
有的時候,當一個資源變化難以使用標準的 RESTful API 來命名铣揉,可以考慮使用一些特殊的 actions 命名饶深。
/{resources}/{resource_id}/actions/{action}
舉個例子,“密碼修改”這個接口的命名很難完全使用名詞來構建路徑逛拱,此時可以引入 action 命名敌厘。
【PUT】? /v1/users/{user_id}/password/actions/modify// 密碼修改
請求方式
可以通過 GET、 POST朽合、 PUT俱两、 PATCH、 DELETE 等方式對服務端的資源進行操作曹步。其中宪彩,GET 用于查詢資源,POST 用于創(chuàng)建資源讲婚,PUT 用于更新服務端的資源的全部信息尿孔,PATCH 用于更新服務端的資源的部分信息,DELETE 用于刪除服務端的資源筹麸。
這里活合,筆者使用“用戶”的案例進行回顧通過 GET、 POST物赶、 PUT白指、 PATCH、 DELETE 等方式對服務端的資源進行操作酵紫。
【GET】? ? ? ? ? /users# 查詢用戶信息列表【GET】? ? ? ? ? /users/1001# 查看某個用戶信息【POST】? ? ? ? /users# 新建用戶信息【PUT】? ? ? ? ? /users/1001# 更新用戶信息(全部字段)【PATCH】? ? ? ? /users/1001# 更新用戶信息(部分字段)【DELETE】? ? ? /users/1001# 刪除用戶信息
查詢參數
RESTful API 接口應該提供參數告嘲,過濾返回結果。其中奖地,offset 指定返回記錄的開始位置橄唬。一般情況下,它會結合 limit 來做分頁的查詢参歹,這里 limit 指定返回記錄的數量仰楚。
【GET】? /{version}/{resources}/{resource_id}?offset=0&limit=20
同時,orderby 可以用來排序泽示,但僅支持單個字符的排序,如果存在多個字段排序蜜氨,需要業(yè)務中擴展其他參數進行支持械筛。
【GET】? /{version}/{resources}/{resource_id}?orderby={field} [asc|desc]
為了更好地選擇是否支持查詢總數,我們可以使用 count 字段飒炎,count 表示返回數據是否包含總條數埋哟,它的默認值為 false。
【GET】? /{version}/{resources}/{resource_id}?count=[true|false]
上面介紹的 offset、 limit赤赊、 orderby 是一些公共參數闯狱。此外,業(yè)務場景中還存在許多個性化的參數抛计。我們來看一個例子哄孤。
【GET】? /v1/categorys/{category_id}/apps/{app_id}?enable=[1|0]&os_type={field}&device_ids={field,field,…}
注意的是,不要過度設計吹截,只返回用戶需要的查詢參數瘦陈。此外,需要考慮是否對查詢參數創(chuàng)建數據庫索引以提高查詢性能波俄。
狀態(tài)碼
使用適合的狀態(tài)碼很重要晨逝,而不應該全部都返回狀態(tài)碼 200,或者隨便亂使用懦铺。這里捉貌,列舉筆者在實際開發(fā)過程中常用的一些狀態(tài)碼,以供參考冬念。
狀態(tài)碼描述
200請求成功
201創(chuàng)建成功
400錯誤的請求
401未驗證
403被拒絕
404無法找到
409資源沖突
500服務器內部錯誤
異常響應
當 RESTful API 接口出現非 2xx 的 HTTP 錯誤碼響應時趁窃,采用全局的異常結構響應信息。
HTTP/1.1 400 Bad RequestContent-Type: application/json{"code":"INVALID_ARGUMENT","message":"{error message}","cause":"{cause message}","request_id":"01234567-89ab-cdef-0123-456789abcdef","host_id":"{server identity}","server_time":"2014-01-01T12:00:00Z"}
請求參數
在設計服務端的 RESTful API 的時候刘急,我們還需要對請求參數進行限制說明棚菊。例如一個支持批量查詢的接口,我們要考慮最大支持查詢的數量叔汁。
【GET】? ? /v1/users/batch?user_ids=1001,1002// 批量查詢用戶信息參數說明- user_ids: 用戶ID串统求,最多允許20個。
此外据块,在設計新增或修改接口時码邻,我們還需要在文檔中明確告訴調用者哪些參數是必填項,哪些是選填項另假,以及它們的邊界值的限制像屋。
【POST】? ? /v1/users// 創(chuàng)建用戶信息請求內容{"username":"lgz",// 必填, 用戶名稱, max 10"realname":"梁桂釗",// 必填, 用戶名稱, max 10"password":"123456",// 必填, 用戶密碼, max 32"email":"lianggzone@163.com",// 選填, 電子郵箱, max 32"weixin":"LiangGzone",// 選填,微信賬號, max 32"sex":1// 必填, 用戶性別[1-男 2-女 99-未知]}
響應參數
針對不同操作边篮,服務端向用戶返回的結果應該符合以下規(guī)范己莺。
【GET】? ? /{version}/{resources}/{resource_id}// 返回單個資源對象【GET】? ? /{version}/{resources}// 返回資源對象的列表【POST】? ? /{version}/{resources}// 返回新生成的資源對象【PUT】? ? /{version}/{resources}/{resource_id}// 返回完整的資源對象【PATCH】? /{version}/{resources}/{resource_id}// 返回完整的資源對象【DELETE】? /{version}/{resources}/{resource_id}// 狀態(tài)碼 200,返回完整的資源對象戈轿。// 狀態(tài)碼 204凌受,返回一個空文檔
如果是單條數據,則返回一個對象的 JSON 字符串思杯。
HTTP/1.1 200 OK{"id":"01234567-89ab-cdef-0123-456789abcdef","name":"example","created_time": 1496676420000,"updated_time": 1496676420000,? ? ...}
如果是列表數據胜蛉,則返回一個封裝的結構體。
HTTP/1.1 200 OK{"count":100,"items":[? ? ? ? {"id":"01234567-89ab-cdef-0123-456789abcdef","name":"example","created_time": 1496676420000,"updated_time": 1496676420000,? ? ? ? ? ? ...? ? ? ? },? ? ? ? ...? ? ]}
一個完整的案例
最后,我們使用一個完整的案例將前面介紹的知識整合起來誊册。這里领突,使用“獲取用戶列表”的案例。
【GET】? ? /v1/users?[&keyword=xxx][&enable=1][&offset=0][&limit=20] 獲取用戶列表功能說明:獲取用戶列表請求方式:GET參數說明- keyword: 模糊查找的關鍵字案怯。[選填]-enable: 啟用狀態(tài)[1-啟用 2-禁用]君旦。[選填]- offset: 獲取位置偏移,從 0 開始殴泰。[選填]-limit: 每次獲取返回的條數于宙,缺省為 20 條,最大不超過 100悍汛。 [選填]響應內容HTTP/1.1 200 OK{"count":100,"items":[? ? ? ? {"id":"01234567-89ab-cdef-0123-456789abcdef","name":"example","created_time": 1496676420000,"updated_time": 1496676420000,? ? ? ? ? ? ...? ? ? ? },? ? ? ? ...? ? ]}失敗響應HTTP/1.1 403 UC/AUTH_DENIEDContent-Type: application/json{"code":"INVALID_ARGUMENT","message":"{error message}","cause":"{cause message}","request_id":"01234567-89ab-cdef-0123-456789abcdef","host_id":"{server identity}","server_time":"2014-01-01T12:00:00Z"}錯誤代碼- 403 UC/AUTH_DENIED? ? 授權受限
作者:趙客縵胡纓v吳鉤霜雪明
鏈接:http://www.reibang.com/p/a88d07ad1493
來源:簡書
簡書著作權歸作者所有捞魁,任何形式的轉載都請聯(lián)系作者獲得授權并注明出處。