背景
過五關(guān),斬六將佑吝,一路殺到了最后一面坐昙,很高興加入了素有“武林正宗少林派”的阿里,而阿里的技術(shù)又充滿著黑科技感芋忿。不過最近我接手了一個老系統(tǒng)時炸客,發(fā)現(xiàn)其中對于REST的應(yīng)用可能有些誤區(qū),于是才想寫一篇關(guān)于REST的介紹戈钢,以便分享給團隊中其他的同學(xué)痹仙。
本篇文章主要包括以下幾個方面:
- REST的概念是什么
- 我們?yōu)槭裁葱枰猂EST
- 前端渲染和后端渲染的優(yōu)勢是什么
- 什么樣的REST才是"正宗"的
一、REST的概念是什么
- 維基百科
表現(xiàn)層狀態(tài)轉(zhuǎn)換(REST殉了,英文:Representational State Transfer)是Roy Thomas Fielding博士于2000年在他的博士論文中提出來的一種萬維網(wǎng)軟件架構(gòu)風(fēng)格开仰,目的是便于不同軟件/程序在網(wǎng)絡(luò)(例如互聯(lián)網(wǎng))中互相傳遞信息。
論文地址:Architectural Styles and the Design of Network-based Software Architectures
REST章節(jié):Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)
- 知乎
[資源]表現(xiàn)層狀態(tài)轉(zhuǎn)換(REST,英文:[Resource] Representational State Transfer)众弓,通俗翻譯為:資源在網(wǎng)絡(luò)中以某種表現(xiàn)形式進行狀態(tài)轉(zhuǎn)移恩溅。
Resource:資源,即數(shù)據(jù)(網(wǎng)絡(luò)的核心)谓娃,比如 goods脚乡,fruits等;
Representational:某種表現(xiàn)形式滨达,比如用JSON奶稠,XML,JPEG等捡遍;
State Transfer:狀態(tài)變化窒典,通過HTTP動詞實現(xiàn)。
二稽莉、我們?yōu)槭裁葱枰猂EST
在前面介紹了REST的概念瀑志,不過對于初次接觸REST的同學(xué)來說,可能會覺得晦澀難懂污秆,因為只有幾個新的名詞概念解釋而已劈猪,對于我們來說它又有什么用呢?
在介紹REST的作用之前良拼,我們先了解一下在REST沒有出現(xiàn)之前的Web開發(fā)是什么樣的?
早期的Web 項目一般是在服務(wù)器端進行渲染战得,服務(wù)器進程從數(shù)據(jù)庫獲取數(shù)據(jù)后,然后利用后端模板引擎(比如Velocity庸推、Freemaker 等)或者直接在HTML 模板中嵌入后端語言(比如JSP常侦、PHP),將數(shù)據(jù)加載進來生成HTML贬媒,然后通過網(wǎng)絡(luò)傳輸?shù)接脩舻臑g覽器中聋亡,最后被瀏覽器解析成可見的頁面。具體的過程如下圖所示:
此時大多數(shù)服務(wù)器架構(gòu)都是這種 MVC 模式际乘,前端只需要一次HTTP請求就可以返回整個頁面內(nèi)容坡倔,加載速度可能會稍微快些。但是它的缺點也非常明顯脖含,前端寫完靜態(tài)頁面罪塔,要讓后臺去套模板,每次前端稍有改動养葵,后臺對應(yīng)的模板頁面同時也需要改動征堪,而且頁面中可能會包含大量復(fù)雜的 JS代碼,比如美工同學(xué)(當(dāng)時的前端)需要通過JS寫界面的交互关拒,而后端同學(xué)又需要通過JS實現(xiàn)數(shù)據(jù)的渲染佃蚜,非常地麻煩庸娱。
當(dāng)然事情麻煩歸麻煩,但還不至于引發(fā)新的技術(shù)革命爽锥,而真正推動REST發(fā)展的是移動互聯(lián)網(wǎng)的出現(xiàn)涌韩。由于多終端設(shè)備的兼容性需求,從前的服務(wù)端渲染已經(jīng)很難滿足要求了氯夷。服務(wù)端不可能針對每一個Client渲染一套界面臣樱,如果服務(wù)端只提供需要的數(shù)據(jù),而具體界面的渲染完全交給具體的Client來完成腮考,因此催生了REST的發(fā)展和普及雇毫。
RESTful可以通過一套統(tǒng)一的接口為 Web、iOS和Android提供服務(wù)踩蔚,另外對于很多平臺來說(比如像Facebook棚放,Twiter、微博馅闽、微信等開放平臺)飘蚯,它們不需要有顯式的前端,只需要一套提供服務(wù)的接口福也,于是RESTful便是它們最好的選擇局骤。
三、前端渲染和后端渲染的優(yōu)勢是什么
隨著前端渲染引擎的發(fā)展暴凑,新興的Angular峦甩,React,Vue等的出現(xiàn)现喳,真正地實現(xiàn)了前后端分離解耦:前端專注于UI凯傲,負(fù)責(zé)View和Controller層,后端專注于業(yè)務(wù)/數(shù)據(jù)處理嗦篱,負(fù)責(zé)Model層冰单,兩端通過設(shè)計好的REST API進行交互。
參考文章
https://github.com/lifesinger/blog/issues/184
https://wenku.baidu.com/view/4c7b010c17fc700abb68a98271fe910ef12dae01
雖然現(xiàn)在前后端分離早已不是什么新鮮話題默色,不過前后端分離到底有什么樣的優(yōu)勢呢球凰?我們可以對比一下各自的優(yōu)點。
后端渲染的優(yōu)點:
- 1腿宰、對搜索引擎友好,這樣做有利于 SEO缘厢。
- 2吃度、加載時間短,后端渲染加載完成后就直接顯示HTML贴硫,但前端渲染在加載完成后還需要有段js 渲染的時間椿每。
前端渲染的優(yōu)點:
- 1伊者、讓前后端的職責(zé)更清晰,分工更合理高效间护。前后端業(yè)務(wù)分離亦渗,后端只需要提供數(shù)據(jù)接口,前端在開發(fā)時也不需要部署對應(yīng)的后端環(huán)境汁尺,可以通過Mock數(shù)據(jù)進行并發(fā)開發(fā)法精。
- 2、計算量轉(zhuǎn)移痴突,原本由服務(wù)器執(zhí)行的渲染任務(wù)轉(zhuǎn)移給了客戶端搂蜓,這在大量用戶訪問的時候大大減輕后端的壓力。讓后端專注做后端應(yīng)該做的事情辽装,性能將大大提高帮碰,因為服務(wù)器做的事情確實減小了。
前后端分離+Node層優(yōu)點:
- 通過 Node拾积,Web Server 層也是 JavaScript 代碼殉挽,這意味著部分代碼可前后復(fù)用,需要 SEO 的場景可以在服務(wù)端同步渲染拓巧,由于異步請求太多導(dǎo)致的性能問題也可以通過服務(wù)端來緩解斯碌,結(jié)合了前兩種模式的優(yōu)點。
言歸正傳玲销,前面介紹了什么是REST输拇,也介紹了它的優(yōu)勢,接下來詳細(xì)介紹REST的具體內(nèi)容贤斜。
四策吠、什么樣的REST才是"正宗"的
無狀態(tài)原則
遵循REST范式的系統(tǒng)是無狀態(tài)的,這意味著服務(wù)器不需要知道客戶端處于什么狀態(tài)瘩绒,反之亦然猴抹。這樣,即使沒有看到以前的消息锁荔,服務(wù)器和客戶端都可以理解收到的任何消息蟀给。這種的無狀態(tài)約束是通過使用資源來實現(xiàn)的。資源是Web中的特定名詞——它描述了任何你可能需要存儲或發(fā)送到其他服務(wù)的對象或文檔阳堕。
無狀態(tài)原則是RESTful架構(gòu)設(shè)計中一個非常重要的原則跋理,無狀態(tài)是相對于有狀態(tài)而言的,我們首先看一下什么是有狀態(tài)的恬总。
Web服務(wù)的狀態(tài)一般指的是請求的狀態(tài)前普,是客戶端和服務(wù)端進行交互操作時所留下來的公共信息(比如,用戶的信息等)壹堰。這些信息可以被指定在不同的作用域中(如request拭卿、session骡湖、application等),通常由服務(wù)端來保存這些信息峻厚。
而無狀態(tài)的Web服務(wù)是指每一個Web請求都是獨立的响蕴,服務(wù)端沒有保存任何客戶端的狀態(tài)信息,所以客戶端發(fā)送的請求必須包含有能夠讓服務(wù)端理解請求的全部信息惠桃。
另外由于REST系統(tǒng)通過資源上的標(biāo)準(zhǔn)操作進行交互浦夷,因此它們不依賴于接口的實現(xiàn),使得RESTful應(yīng)用程序具有可靠性刽射、快速性和可擴展性军拟。
前后端通信機制
1、請求方式
REST要求客戶端向服務(wù)端發(fā)出請求以獲得或修改服務(wù)器上的數(shù)據(jù)誓禁。請求通常由以下部分組成:
- 一個HTTP動詞懈息,它定義了要執(zhí)行的操作類型
- 一個頭部,它允許客戶端傳遞關(guān)于請求的信息
- 一條資源的路徑
- 一個包含數(shù)據(jù)的可選消息主體
(1)對于HTTP動詞
在REST系統(tǒng)中我們使用4個基本HTTP動詞來與資源進行交互:
- GET - 檢索特定資源(通過id)或資源集合
- POST - 創(chuàng)建一個新資源
- PUT - 更新特定資源(通過ID)
- DELETE - 按ID刪除特定資源
(2)對于請求頭部
客戶端向服務(wù)端發(fā)送它能夠接收的內(nèi)容的類型摹恰,而該類型是通過一個叫Accept的字段發(fā)送的辫继。通過這種方式可以確保服務(wù)端不會發(fā)送客戶端無法理解或者無法處理的數(shù)據(jù)。
用于指定Accept字段的類型為MIME類型俗慈,它是由一個type和一個subtype通過斜線(/)分隔組成的姑宽。你可以在MDN Web文檔中查看更多關(guān)于MIME類型的介紹。
例如闺阱,包含HTML的文本文件類型指定為text/html炮车,而包含CSS的文本文件需要指定為text/css,一般的文本文件將被指定為text/plain(如果不指定酣溃,則默認(rèn)值為text/plain)瘦穆。假如客戶期待的類型為text/css,而接收到的類型text/plain赊豌,那么客戶端將無法識別它扛或。以下列舉了其他的type和subtype:
- image — image/png, image/jpeg, image/gif
- audio — audio/wav, image/mpeg
- video — video/mp4, video/ogg
- application — application/json, application/pdf, application/xml, application/octet-stream
(3)對于資源路徑
在RESTful API中,每一個請求的動作都必須作用于一個資源路徑上碘饼,所以資源路徑的設(shè)計就是為了讓客戶端能夠理解它所進行的操作是什么熙兔。
通常情況下,路徑的第一部分應(yīng)該是資源的復(fù)數(shù)形式艾恼。RESTful中這種路徑嵌套方式簡單易讀住涉,也更容易理解。示例如下:
https://www.alipay.com/customers/22/orders/11
該RESTful API指向的路徑非常清晰钠绍,因為它具有層次性和自描述性秆吵。該示例中,我們查詢了id號為22這位顧客的一筆訂單五慈,并且該訂單的id號為11纳寂。
路徑必須包含它所需要的能夠準(zhǔn)確定位它所代表的資源位置的信息。但是泻拦,如果引用的資源為列表或集合時毙芜,就不需要再向POST請求添加id這樣的唯一標(biāo)識了,因為在服務(wù)端將為該新對象生成一個唯一標(biāo)識id的争拐。例如向顧客集合中新增一位顧客:
POST https://www.alipay.com/customers
如果我們試圖訪問單個資源腋粥,則需要在路徑后面添加一個id,例如通過指定的id來查詢一位顧客:
GET https://www.alipay.com/customers/:id
或者架曹,通過指定的id來刪除一位顧客:
DELETE https://www.alipay.com/customers/:id
2隘冲、接收內(nèi)容
在服務(wù)器向客戶端發(fā)送數(shù)據(jù)有效載荷的情況下,服務(wù)器必須content-type在響應(yīng)的頭部包含一個绑雄。這個content-type頭域告訴客戶端它在響應(yīng)主體中發(fā)送的數(shù)據(jù)的類型展辞。這些內(nèi)容類型是MIME類型,就像它們在accept請求頭的字段中一樣万牺。該content-type服務(wù)器在響應(yīng)發(fā)送回應(yīng)的客戶機中指定的選項之一accept的請求的字段罗珍。
例如,當(dāng)客戶端使用此GET請求訪問具有資源id23的articles資源時:
GET /articles/23 HTTP/1.1
Accept: text/html, application/xhtml
服務(wù)器可能會使用響應(yīng)頭發(fā)回內(nèi)容:
HTTP/1.1 200 (OK)
Content-Type: text/html
這將意味著所請求的內(nèi)容被返回的響應(yīng)體用content-type的text/html脚粟,該客戶表示覆旱,將能夠接受。
3核无、返回狀態(tài)
在服務(wù)器向客戶端發(fā)送數(shù)據(jù)有效載荷的情況下扣唱,服務(wù)器必須content-type在響應(yīng)的頭部包含一個。這個content-type頭域告訴客戶端它在響應(yīng)主體中發(fā)送的數(shù)據(jù)的類型团南。這些內(nèi)容類型是MIME類型噪沙,就像它們在accept請求頭的字段中一樣。該content-type服務(wù)器在響應(yīng)發(fā)送回應(yīng)的客戶機中指定的選項之一accept的請求的字段已慢。
例如曲聂,當(dāng)客戶端使用此GET請求訪問具有資源id23的articles資源時:
GET /articles/23 HTTP/1.1
Accept: text/html, application/xhtml
服務(wù)器可能會使用響應(yīng)頭發(fā)回內(nèi)容:
HTTP/1.1 200 (OK)
Content-Type: text/html
來自服務(wù)器的響應(yīng)包含狀態(tài)代碼,以提醒客戶有關(guān)操作成功的信息佑惠。作為開發(fā)人員朋腋,您不需要知道每個狀態(tài)代碼(其中有很多),但您應(yīng)該知道最常見的狀態(tài)代碼以及它們的使用方式:
狀態(tài)碼 | 含義 |
---|---|
200 (OK) | 這是成功HTTP請求的標(biāo)準(zhǔn)響應(yīng)膜楷。 |
201 (CREATED) | 這是導(dǎo)致成功創(chuàng)建項目的HTTP請求的標(biāo)準(zhǔn)響應(yīng)旭咽。 |
204 (NO CONTENT) | 這是成功HTTP請求的標(biāo)準(zhǔn)響應(yīng),響應(yīng)正文中沒有任何內(nèi)容被返回赌厅。 |
400 (BAD REQUEST) | 由于請求語法錯誤穷绵,大小過大或其他客戶端錯誤,無法處理該請求特愿。 |
403 (FORBIDDEN) | 客戶端沒有權(quán)限訪問此資源仲墨。 |
404 (NOT FOUND) | 此時無法找到該資源勾缭。它可能已被刪除,或尚不存在目养。 |
500 (INTERNAL SERVER ERROR) | 如果沒有更多可用的特定信息俩由,則通用答案意外失敗。 |
對于每個HTTP動詞癌蚁,服務(wù)器在成功時應(yīng)返回預(yù)期的狀態(tài)代碼:
- GET - 返回200(OK)幻梯。
- POST - 返回201(創(chuàng)建)。
- PUT - 返回200(OK)努释。
- DELETE - 返回204(無內(nèi)容)碘梢。如果操作失敗,則返回可能對應(yīng)于遇到的問題的最具體的狀態(tài)碼伐蒂。
4煞躬、CRUD示例說明
現(xiàn)在我們需要設(shè)計一個班級管理的系統(tǒng),其中需要有班級信息和班級的學(xué)生信息饿自。那我們該怎么為它設(shè)計REST接口呢汰翠?可以按照以下幾點思考:
- 使用什么樣的請求方式?
- 服務(wù)端返回是什么樣的昭雌?
- 通過什么樣的
content-type
傳輸內(nèi)容复唤?
- 首先定義班級和學(xué)生的數(shù)據(jù)模型如下。
{
“class”: {
"id": <Integer>,
“name”: <String>,
“num”: <Integer>
}
}
{
“ student”: {
"id": <Integer>,
“name”: <String>,
“age”: <Integer>
}
}
- 接口請求/響應(yīng)的定義烛卧。
GET請求:
接口 | 請求方式 | 傳輸格式(Content-type) | 返回狀態(tài) |
---|---|---|---|
/classes | GET | application/json | 200 (OK) |
/classes/:id | GET | application/json | 200 (OK) |
/classes/:id/students | GET | application/json | 200 (OK) |
/classes/:id/students/:id | GET | application/json | 200 (OK) |
POST請求:
接口 | 請求方式 | 傳輸格式(Content-type) | 返回狀態(tài) |
---|---|---|---|
/classes | POST | application/json | 201 (CREATED) |
/classes/:id/students | POST | application/json | 201 (CREATED) |
PUT請求:
接口 | 請求方式 | 傳輸格式(Content-type) | 返回狀態(tài) |
---|---|---|---|
/classes/:id | PUT | application/json | 200 (OK) |
/classes/:id/students/:id | PUT | application/json | 200 (OK) |
DELETE請求:
接口 | 請求方式 | 傳輸格式(Content-type) | 返回狀態(tài) |
---|---|---|---|
/classes/:id | DELETE | application/json | 204 (NO CONTENT) |
/classes/:id/students/:id | DELETE | application/json | 204 (NO CONTENT) |
小結(jié)
本文介紹了REST的相關(guān)概念佛纫,同時介紹了我們?yōu)槭裁葱枰猂EST,分析了前端渲染和后端渲染的優(yōu)勢是什么总放,然后再介紹了什么樣的REST才是"正宗"的呈宇,最后通過一個示例完整的演示了CRUD的操作。