近日妹子向我求助RESTful API究竟是個什麼東西。緣由是她們公司一個新啓動的項目由於RESTful API起了爭執。服務端同窗堅持要用RESTful API,而前端同窗則認爲服務端用RESTful API就會讓前端的調用變得更麻煩。最終爭議了一下午仍是不了了之。有趣的是他們組的大部分人都不太瞭解REST是個什麼東西。html
實際上一些抽象的東西是不如一些具體的技術好講解的,就像你給新人講面向對象同樣,這東西得靠時間,靠悟。我以前作過開放平臺API的項目。對於RESTful API還算有些瞭解。萬幸沒有丟人,口乾舌燥以後總算講明白一些。但這東西真正理解還得多悟、多思考、多練習。固然,若是你有更好的理解,可在評論區與我留言分享!我會第一時間反饋!前端
REST,即Representational State Transfer的縮寫,翻譯過來就是"表現層狀態轉化"。不得不認可,我在剛開始看到這個名詞的時候是一臉懵逼。好了,如今咱們放棄對這個名詞的理解。git
實際上,REST只是一種軟件架構風格。注意了,它並非一種具體的技術。而更像是一種約束與規範性的東西,它包含了不少原則與限制。而若是一個架構符合REST的原則,就能夠稱它爲RESTful架構。程序員
在REST中最重要的一個概念就是資源。在面向對象的世界裏,咱們提倡萬物皆對象,而在REST的世界裏則是萬物皆資源。所謂"資源",就是網絡上的一個實體,或者說是網絡上的一個具體信息。它能夠是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的存在。github
"資源"是一種信息實體,它能夠有多種外在表現形式。咱們把"資源"具體呈現出來的形式,叫作它的"表現層"數據庫
好比,文本能夠用txt格式表現,也能夠用HTML格式、XML格式、JSON格式表現,甚至能夠採用二進制格式;圖片能夠用JPG格式表現,也能夠用PNG格式表現。json
訪問一個網站,就表明了客戶端和服務器的一個互動過程。在這個過程當中,勢必涉及到數據和狀態的變化。後端
當下的互聯網通訊協議HTTP協議,是一個無狀態協議。這意味着,全部的狀態都保存在服務器端。所以,若是客戶端想要操做服務器,必須經過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是創建在表現層之上的,因此就是"表現層狀態轉化"。api
在HTTP協議裏面,就可使用HTTP動詞來對服務器端資源進行操做,實現「表現層狀態轉化」。如:GET、POST、PUT、DELETE。它們分別對應四種基本操做:GET用來獲取資源,POST用來新建資源(也能夠用於更新資源),PUT用來更新資源,DELETE用來刪除資源。緩存
以網站中常見的用戶CRUD操做爲例:
如今,咱們再回過頭來理解REST(表現層狀態轉化)——REST是一種經過表現層來操做改變資源的狀態的軟件架構風格。
RESTful API 就是REST風格的API。它使用URI來描述資源,使用Html、Json、XML等格式表現,經過HTTP動詞來操做資源來實現狀態轉化,使用HTTP狀態碼反映處理結果。
URI一般由三部分組成:
訪問資源的命名機制;
存放資源的主機名;
資源自身的名稱。
例如:https://localhost/post/1 (對應URLhttps://localhost/post/1.html)
咱們能夠這樣解釋它:
這是一個能夠經過https協議訪問的資源,
位於主機 localhost上,
經過「post/1」能夠對該資源進行惟一標識(注意,這個不必定是完整的路徑)
注意:以上三點只不過是對實例的解釋,以上三點並非URI的必要條件,URI只是一種概念,怎樣實現無所謂,只要它惟一標識一個資源就能夠了。URI只表明資源的實體,不表明它的形式。嚴格地說,如上面網址最後的".html"後綴名是沒必要要的,由於這個後綴名錶示格式,屬於"表現層"範疇,而URI應該只表明"資源"的位置。
經常使用的HTTP動詞有下面這些
GET:從服務器取出資源(一項或多項)。——冪等
POST:在服務器新建一個資源。——非冪等
PUT:在服務器更新資源(客戶端提供改變後的完整資源)。——冪等
PATCH:在服務器更新資源(客戶端提供改變的屬性)。——冪等
DELETE:從服務器刪除資源。——冪等
HEAD:獲取資源的元數據。
OPTIONS:獲取信息,關於資源的哪些屬性是客戶端能夠改變的。
HTTP協議自己就給咱們提供了豐富的狀態碼,以用來反映服務器端處理的結果。而在真正使用中絕大對數人僅僅瞭解會使用200,404,500之流。這就比如36板斧,你始終是會那三板斧。而RESTful Api規範的HTTP狀態碼的使用,使HTTP協議的優勢發揮到了極致。
例如:
注意:當狀態碼是4或5開頭的時候就應該像用戶返回錯誤信息。通常來講,返回的信息中將error做爲鍵名,出錯信息做爲鍵值便可。
{ error: "Invalid API key" }
以下表是經常使用的HTTP狀態碼和描述
CODE | HTTP Operation | Body Contents | Decription |
---|---|---|---|
200 | GET,PUT | 資源 | 操做成功 |
201 | POST | 資源,元數據 | 對象建立成功 |
202 | POST,PUT,DELETE,PATCH | N/A | 請求已被接受 |
204 | DELETE,PUT,PATCH | N/A | 操做已經執行成功,可是沒有返回結果 |
301 | GET | link | 資源已被移除 |
303 | GET | link | 重定向 |
304 | GET | N/A | 資源沒有被修改 |
400 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 參數列表錯誤(缺乏,格式不匹配) |
401 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 未受權 |
403 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 訪問受限,受權過時 |
404 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 資源,服務未找到 |
405 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 不容許的HTTP方法 |
409 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 資源衝突,或資源被鎖定 |
415 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 不支持的數據(媒體)類型 |
429 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 請求過多被限制 |
500 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 系統內部錯誤 |
501 | GET,POST,PUT,DELETE,PATCH | 錯誤提示(消息) | 接口未實現 |
咱們以Web網站中經常使用的用戶增刪查改成例。設計普通的API接口完成增刪查改大體以下:
//添加用戶 http://localhost/createuser //刪除id爲1的用戶 http://localhost/deleteuser?userid=1 //獲取用戶列表 http://localhost/getuser //獲取id爲1的用戶 http://localhost/getuser?userid=1 //更新id爲1的用戶 http://localhost/updateuser?userid=1
咱們經過調用上面不一樣的url傳遞響應的參數來完成用戶的增刪查改。
而使用RESTful 風格的api該如何完成呢?
在這個例子中很明顯,用戶就是咱們的資源,使用uri來描述資源就是
http://localhost/user
表現層能夠是Json也能夠是xml或者其它。
咱們使用HTTP的動詞來操做用戶這個資源。
http://localhost/user
表明查詢用戶列表http://localhost/user/1
表明查詢id爲1的用戶http://localhost/user
表明建立一個用戶http://localhost/user/1
表明修改id爲1的用戶http://localhost/user/1
表明刪除id爲1的用戶。能夠看到這種風格看起來要更爲優雅與簡潔,面向資源,一目瞭然,具備自解釋性,充分的發揮了HTTP協議的優勢。
由於"資源"表示一種實體,因此應該是名詞,URI不該該有動詞,動詞應該放在HTTP協議中。
舉例來講,某個URI是/posts/show/1,其中show是動詞,這個URI就設計錯了,正確的寫法應該是/posts/1,而後用GET方法表示show。
若是某些動做是HTTP動詞表示不了的,你就應該把動做作成一種資源。好比網上匯款,從帳戶1向帳戶2匯款500元,錯誤的URI是:
POST /accounts/1/transfer/500/to/2
正確的寫法是把動詞transfer改爲名詞transaction,資源不能是動詞,可是能夠是一種服務:
POST /transaction HTTP/1.1 Host: 127.0.0.1 from=1&to=2&amount=500.00
另外一個設計誤區,就是在URI中加入版本號:
http://localhost/app/1.0/foo http://localhost/app/1.1/foo http://localhost/app/2.0/foo
由於不一樣的版本,能夠理解成同一種資源的不一樣表現形式,因此應該採用同一個URI。版本號能夠在HTTP請求頭信息的Accept字段中進行區分(參見Versioning REST Services):
Accept: localhost.foo+json; version=1.0 Accept: localhost.foo+json; version=1.1 Accept: localhostfoo+json; version=2.0
注意,面向資源不等於面向單表操做。不知道爲何不少人會把資源對應到數據庫裏的單張表。其實他們沒有任何關係。資源能夠是一個文件,能夠是緩存裏的數據,也能夠是數據庫裏多張表聚合的結果。好比用戶這個資源。一般咱們設計數據庫的時候出於性能或範式的考慮用戶的信息不會放在一張表裏。可是在API設計的時候用戶就是一個資源,這個資源的數據有可能來自一張表也有多是多張表,甚至緩存。
跟萬物皆對象同樣,使用「萬物皆資源」的思想設計實際項目中的API時,常常會遇到一個問題就是「這玩意究竟是個什麼資源?………………算了,我就直接寫吧,無論什麼風格了」
其實在真正理解了REST後,這些都不是什麼無解的難題,若是無解,那隻能說明你還沒真正理解,抽象成資源的能力還不到家:
登陸(login)和登出(logout)其實本質上只是對session資源的建立和刪除;
//登陸——使用HTTP POST請求 POST /localhost/session //登出——使用HTTP DELETE請求 DELETE /localhost/session
咱們能夠把search自己抽象成資源,使用POST建立,若是不需持久化,能夠直接在Response中返回結果,若是須要(如翻頁、長期緩存等),直接保存搜索結果並303跳轉到資源地址就好了;
//HTTP POST建立資源search POST /localhost/search
批量操做id多到連url都寫不下的請求,應該建立task,用GET返回task狀態甚至執行進度;
//HTTP POST建立Task POST /localhost/task //HTTP GET獲取TASK執行結果 GET /localhost/task
任何一門技術或者思想都有其優缺點,雖然其誕生的初衷都是爲了解決咱們的問題,而不是帶來更大的災難。REST一樣如此。它的優勢很明顯,優雅、規範,行爲和資源分離,更容易理解。
可是也有其缺點,它面向資源,這種設計思路是反程序員直覺的,由於在本地業務代碼中仍然是一個個的函數,是動做,但表如今接口形式上則徹底是資源的形式,對於後端開發人員要求高,有些業務邏輯難以被抽象爲資源的增刪改查。甚至有些時候RESTful實際上是個效率很低的東西,爲了實現一個資源,你須要定義它的一套方式,若是要聯合查詢又會要求對其衍生或定義一個新的資源。它提供的接口通常是「粗」粒度的,它一般返回的都是完整的數據模型,難以查詢符合特殊要求的數據,有些特殊的業務要比普通的API須要更屢次HTTP請求。
REST面對的疑問跟當年剛開始流行面向對象時的狀況是同樣的。它適合不少狀況,但並不適合全部狀況。它更適合與一些開放平臺API,如新浪微博、GitHub、京東、淘寶開放平臺等,開放API之因此開放,就是由於它不知道你到底須要什麼返回結果,既然不知道,那麼我乾脆都返回給你,有客戶端自由組合成本身想要的數據。而對於內部開發,有其侷限性,內部開發因爲需求很是明確,有些時候出於性能或靈活性的考慮,服務端簡單粗暴的丟出來完整的數據模型由客戶端本身處理顯然是不合適的。
對於一些內部的開發,適合用RESTful API的咱們仍然能夠用,對於一些不合適的,咱們仍然能夠借鑑一些RESTFul中的優勢,來設計咱們的API。好比簡潔的URI(每一個人看到一坨超長的API地址,心裏都是拒絕的),充分利用HTTP狀態碼等。
RESTful API是REST風格的API,它是一種API設計風格,規範了API設計中的一些原則。它讓咱們的API更加優雅、規範。但也尤爲缺點,在實際使用過程當中咱們應該充分的取理解它,綜合考量其使用場景。
若是你們想要取學習並使用它,建議能夠參考Github開放API 或者Elasticsearch API,看一看他們是如何設計的API,對於本身項目中的每個場景多思考,去網上一些開源RESTful API找一找有沒有相同場景的例子。
不少人會盲目追新,又對REST的概念和理念只知其一;不知其二,最後搞出一個半吊子的怪胎,不只沒有設計出優雅規範的API,甚至還引發了更大的麻煩,最後還自我標榜用了流行的RESTful API。
其實REST規範最終仍是爲了開發者和軟件產品服務的,若是它能帶來便利、減小混亂,就值得用;反之,若是帶來的麻煩比解決的還多,跟風追流行就不可取了。其它任何技術也是如此!