前言
前后端分離已經(jīng)是業(yè)界所共識(shí)的一種開發(fā)/部署模式了。前端和后端人員的分工越來越明確,他們之間如何協(xié)同工作變得越來越重要。這種模式下,我們通過API來解耦前端和后端開發(fā)過程。因此API是前后端必須共同理解的語(yǔ)言。雙方只需要約定數(shù)據(jù)接口,而不必在代碼層面有任何的耦合。預(yù)先確定一個(gè)雙方都能夠理解的編程風(fēng)格是前后端分離非常重要的一環(huán)。
什么是restful編程風(fēng)格

Fielding博士的那篇經(jīng)典論文(中文版)對(duì)萬(wàn)維網(wǎng)架構(gòu)的貢獻(xiàn)可謂是居功至偉??上攵?,當(dāng)REST一詞變得流行起來之后,其濫用甚至是“掛羊頭賣狗肉”的現(xiàn)象是不可避免的。而糟糕的是,對(duì)于那些沒有時(shí)間、也沒有耐心去仔細(xì)閱讀該論文的人來說,可能就會(huì)在看過或用過某些號(hào)稱具有REST風(fēng)格的應(yīng)用之后對(duì)REST本身產(chǎn)生錯(cuò)誤的理解,進(jìn)而在錯(cuò)誤的思想指導(dǎo)之下錯(cuò)誤地運(yùn)用REST。這正是其創(chuàng)造者本身所不愿意看到的。
Wikipedia: 表征性狀態(tài)傳輸(英文:Representational State Transfer,簡(jiǎn)稱REST)是Roy Fielding博士于2000年在他的博士論文中提出來的一種軟件架構(gòu)風(fēng)格。
Roy Fielding是HTTP協(xié)議(1.0版和1.1版)的主要設(shè)計(jì)者,事實(shí)上HTTP 1.1規(guī)范正是基于REST架構(gòu)風(fēng)格的指導(dǎo)原理來設(shè)計(jì)的。需要注意的是,REST是一種設(shè)計(jì)風(fēng)格而不是標(biāo)準(zhǔn),如果一個(gè)架構(gòu)符合REST原則,我們就稱它為RESTful架構(gòu)。
為什么要寫成restful風(fēng)格
在「遠(yuǎn)古時(shí)代」前端后端是融合在一起的,比如之前的PHP,JSP,ASP等等。近年來隨著移動(dòng)互聯(lián)網(wǎng)的飛速發(fā)展,各種類型的Client端層出不窮,就需要通過一套統(tǒng)一的接口分別為Web,iOS和Android乃至桌面端提供服務(wù)。另外對(duì)于廣大平臺(tái)來說,比如Facebook platform,微博開放平臺(tái),微信公共平臺(tái)等,它們不需要有顯式的前端,只需要一套提供服務(wù)的接口,于是RESTful更是它們最好的選擇。
理解REST三要素
- Resource:資源,即數(shù)據(jù)。比如newsfeed,friends,order等;
- Representational:某種表現(xiàn)形式,比如用JSON,XML,JPEG等;
- State Transfer:狀態(tài)變化。通過HTTP動(dòng)詞實(shí)現(xiàn)。

然后再來理解一個(gè)具體的RESTful架構(gòu)——面向資源的架構(gòu)(Resource-Oriented Architecture,ROA):
資源是由URI來指定。所謂「上網(wǎng)」,就是與互聯(lián)網(wǎng)上一系列的「資源」互動(dòng),調(diào)用它的URI。
對(duì)資源的操作包括獲取、創(chuàng)建、修改和刪除資源,這些操作正好對(duì)應(yīng)HTTP協(xié)議提供的GET、POST、PUT和DELETE方法。
通過操作資源的表現(xiàn)形式來操作資源。具體表現(xiàn)形式,應(yīng)該在HTTP請(qǐng)求的頭信息中用Accept和Content-Type字段指定。
資源的表現(xiàn)形式則是XML或者HTML,取決于讀者是機(jī)器還是人,是消費(fèi)web服務(wù)的客戶軟件還是web瀏覽器。當(dāng)然也可以是任何其他的格式。
怎樣做
應(yīng)用于Web服務(wù),符合REST設(shè)計(jì)風(fēng)格的Web API稱為RESTful API。它從以下三個(gè)方面資源進(jìn)行定義:
直觀簡(jiǎn)短的資源地址:URI,比如:http://example.com/resources/
;每一個(gè)URI代表一種資源;
傳輸?shù)馁Y源:Web服務(wù)接受與返回的互聯(lián)網(wǎng)媒體類型,比如:JSON,XML,YAML等。
對(duì)資源的操作:Web服務(wù)在該資源上所支持的一系列請(qǐng)求方法(比如:POST,GET,PUT或DELETE)。
來個(gè)圖,
Restful API設(shè)計(jì)要點(diǎn)
1.使用名詞而不是動(dòng)詞
Resource
資源
GET讀
POST創(chuàng)建
PUT修改
DELETE刪除
/cars 返回 cars集合 創(chuàng)建新的資源 批量更新cars 刪除所有cars
/cars/711 返回特定的car 該方法不允許(405) 更新一個(gè)指定的資源 擅長(zhǎng)指定資源
不要使用:
/getAllCars
/createNewCar
/deleteAllRedCars
2.Get方法和查詢參數(shù)不應(yīng)該涉及狀態(tài)改變
使用PUT, POST 和DELETE 方法 而不是 GET 方法來改變狀態(tài),不要使用GET 進(jìn)行狀態(tài)改變:
GET /users/711?activate
GET /users/711/activate
3.使用復(fù)數(shù)名詞
不要混淆名詞單數(shù)和復(fù)數(shù),為了保持簡(jiǎn)單,只對(duì)所有資源使用復(fù)數(shù)。
/cars 而不是 /car
/users 而不是 /user
/products 而不是 /product
/settings 而部署 /setting
4. 使用子資源表達(dá)關(guān)系
如果一個(gè)資源與另外一個(gè)資源有關(guān)系,使用子資源:
GET /cars/711/drivers/ 返回 car 711的所有司機(jī)
GET /cars/711/drivers/4 返回 car 711的4號(hào)司機(jī)
5.使用Http頭聲明序列化格式
在客戶端和服務(wù)端,雙方都要知道通訊的格式,格式在HTTP-Header中指定
Content-Type 定義請(qǐng)求格式
Accept 定義系列可接受的響應(yīng)格式
6.使用HATEOAS
Hypermedia as the Engine of Application State 超媒體作為應(yīng)用狀態(tài)的引擎,超文本鏈接可以建立更好的文本瀏覽:
{
"id": 711,
"manufacturer": "bmw",
"model": "X5",
"seats": 5,
"drivers": [
{
"id": "23",
"name": "Stefan Jauker",
"links": [
{
"rel": "self",
"href": "/api/v1/drivers/23"
}
]
}
]
}
注意href指向下一個(gè)URL
7.為集合提供過濾 排序 選擇和分頁(yè)等功能
Filtering過濾:
使用唯一的查詢參數(shù)進(jìn)行過濾:
GET /cars?color=red 返回紅色的cars
GET /cars?seats<=2 返回小于兩座位的cars集合
Sorting排序:
允許針對(duì)多個(gè)字段排序
GET /cars?sort=-manufactorer,+model
這是返回根據(jù)生產(chǎn)者降序和模型升序排列的car集合
Field selection
移動(dòng)端能夠顯示其中一些字段,它們其實(shí)不需要一個(gè)資源的所有字段,給API消費(fèi)者一個(gè)選擇字段的能力,這會(huì)降低網(wǎng)絡(luò)流量,提高API可用性。
GET /cars?fields=manufacturer,model,id,color
Paging分頁(yè)
使用 limit 和offset.實(shí)現(xiàn)分頁(yè),缺省limit=20 和offset=0;
GET /cars?offset=10&limit=5
為了將總數(shù)發(fā)給客戶端,使用訂制的HTTP頭: X-Total-Count.
鏈接到下一頁(yè)或上一頁(yè)可以在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
使得API版本變得強(qiáng)制性,不要發(fā)布無(wú)版本的API,使用簡(jiǎn)單數(shù)字,避免小數(shù)點(diǎn)如2.5.
一般在Url后面使用?v
/blog/api/v1
9. 使用Http狀態(tài)碼處理錯(cuò)誤
如果你的API沒有錯(cuò)誤處理是很難的,只是返回500和出錯(cuò)堆棧不一定有用
Http狀態(tài)碼提供70個(gè)出錯(cuò),我們只要使用10個(gè)左右:
200 – OK – 一切正常
201 – OK – 新的資源已經(jīng)成功創(chuàng)建
204 – OK – 資源已經(jīng)成功擅長(zhǎng)
304 – Not Modified – 客戶端使用緩存數(shù)據(jù)
400 – Bad Request – 請(qǐng)求無(wú)效,需要附加細(xì)節(jié)解釋如 "JSON無(wú)效"
401 – Unauthorized – 請(qǐng)求需要用戶驗(yàn)證
403 – Forbidden – 服務(wù)器已經(jīng)理解了請(qǐng)求,但是拒絕服務(wù)或這種請(qǐng)求的訪問是不允許的。
404 – Not found – 沒有發(fā)現(xiàn)該資源
422 – Unprocessable Entity – 只有服務(wù)器不能處理實(shí)體時(shí)使用,比如圖像不能被格式化,或者重要字段丟失。
500 – Internal Server Error – API開發(fā)者應(yīng)該避免這種錯(cuò)誤。
使用詳細(xì)的錯(cuò)誤包裝錯(cuò)誤:
{
"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"
}
]
}
10.允許覆蓋http方法
一些代理只支持POST 和 GET方法, 為了使用這些有限方法支持RESTful API,需要一種辦法覆蓋http原來的方法。
使用訂制的HTTP頭 X-HTTP-Method-Override 來覆蓋POST 方法.
冪等性安全性
HTTP動(dòng)詞
HTTP并沒有定義很多動(dòng)詞來描述web服務(wù)中可能出現(xiàn)的行為,它只用了一個(gè)標(biāo)準(zhǔn)動(dòng)詞集合來處理各種相似情況,從而讓API變得更直觀。每個(gè)動(dòng)詞通過兩種屬性的組合來滿足不同的場(chǎng)景需求。
冪等性:操作可以被重復(fù)執(zhí)行,就算在失敗以后。
安全性:對(duì)客戶端來說操作不會(huì)產(chǎn)生副作用。
GET --冪等、安全
用來從服務(wù)器端讀取狀態(tài)。這個(gè)操作是安全的,所以它可以被執(zhí)行很多次而不會(huì)對(duì)數(shù)據(jù)有任何影響,也就是說執(zhí)行它一次跟執(zhí)行十次是一樣的效果。從冪等性方面來看,多次請(qǐng)求跟單個(gè)請(qǐng)求總能得到相同的結(jié)果。
POST
一般用來在服務(wù)器端創(chuàng)建某種狀態(tài)。這個(gè)操作不具備冪等性跟安全性,所以多次請(qǐng)求會(huì)在服務(wù)器端創(chuàng)建多個(gè)資源。因?yàn)镻OST是不冪等的, 所以不應(yīng)該被用來做跟金錢有關(guān)系的操作,試想一次失敗的請(qǐng)求如果被執(zhí)行多次,那么很可能轉(zhuǎn)賬或者支付也被執(zhí)行了多次。
PUT
雖然它也可以被用來創(chuàng)建狀態(tài),但主要還是用來在服務(wù)器端更新狀態(tài)的。它是冪等的,但不安全,因?yàn)樗鼤?huì)改變服務(wù)端的狀態(tài)。因?yàn)樗膬绲刃?,PUT可以被用來處理跟金錢有關(guān)系的操作。
DELETE
用來在服務(wù)器端刪除狀態(tài)。它也是冪等非安全的,因?yàn)樗鼤?huì)移除服務(wù)端的狀態(tài)。它之所以是冪等的,是因?yàn)橹貜?fù)刪除一個(gè)狀態(tài)的結(jié)果是一樣。
是否一定要用REST
前面說了這么多REST,最后再冷靜下來思考一下,是否一定要用REST,是否一定要嚴(yán)格遵循REST設(shè)計(jì)風(fēng)格。
引用知乎作者淘李福的話
REST本身不是架構(gòu),只是一種架構(gòu)風(fēng)格,理解它的時(shí)候要參考這個(gè)架構(gòu)風(fēng)格出現(xiàn)的環(huán)境所施加的約束條件。
REST的目的是“建立十年內(nèi)不會(huì)過時(shí)的軟件系統(tǒng)架構(gòu)",所以它具備三個(gè)特點(diǎn):
- 狀態(tài)無(wú)關(guān) —— 確保系統(tǒng)的橫向拓展能力
- 超文本驅(qū)動(dòng),F(xiàn)ielding的原話是”hypertext-driven" —— 確保系統(tǒng)的演化能力
- 對(duì) resource 相關(guān)的模型建立統(tǒng)一的原語(yǔ),例如:uri、http的method定義等 —— 確保系統(tǒng)能夠接納多樣而又標(biāo)準(zhǔn)的客戶端
從另外一個(gè)角度看,第一條保證服務(wù)端演化,第三條保證客戶端演化,第二條保證應(yīng)用本身的演化,這實(shí)在是一個(gè)極具抽象能力的方案。
引用列表
https://en.wikipedia.org/wiki/Roy_Fielding
https://blog.jimmylv.info/2015-11-11-what-is-really-rest/
http://www.jdon.com/soa/10-best-practices-for-better-restful-api.html
https://www.oschina.net/translate/what-does-restful-really-mean