1. 程式人生 > >REST,以及RESTful的講解

REST,以及RESTful的講解

1.傳統下的API介面

  http是目前在網際網路上使用最多的協議,沒有之一。
  可是http的創始人一直都覺得,在過去10幾年來,所有的人都在錯誤的使用Http.這句話怎麼說呢?
  如果說你要刪除一個數據,以往的做法通常是 delete/{id} 
  如果你要更新一個數據,可能是Post資料放Body,然後方法是 update/{id}, 或者是artichle/{id}?method=update 
   這種做法讓Roy Fielding很暴燥,他覺得這個世界不該這樣的,所有的人都在誤解而且在嚴重錯誤的誤解Http的設計初衷,好比是發明了火藥卻只用它來做煙花爆竹
   那麼正確的使用方式是什麼呢?如果你要看Rest各種特性,你恐怕真的很難理解Rest,但是如果你看錯誤的使用http的人倒底兒了哪些錯,什麼是Rest就特別容易理解了

。 
  
七宗罪的第一條,混亂。  

   一萬個人心裡有一萬個Url的命名規則,Url是統一資源定位符,重點是資源。而很多人卻把它當成了萬金油,每一個獨立的虛擬的網頁都可以隨意使用,各種操作都能夠迭加。這是混亂的來源之一。
比如:

https://localhost:8080/myweb/getUserById?id=1
https://localhost:8080/myweb/user/getById?id=1
https://localhost:8080/myweb/x/y?id=1

第二條,貪婪。

  有狀態無狀態全部混在一起。特別是在購物車或者是登入的應用中,經常重新整理就丟失帶來的使用者體驗簡直棒棒噠。每一個請求並不能單獨的響應一些功能,很多的功能混雜在一起裡。這是人性貪婪的本質,也是各種Hack

的起源,只要能夠把問題解決掉,總會有人用他認為最方便的方式去解決問題,比如說汽車門把手壞掉了直接系根繩子當把手,emmmm這樣確實很棒啊。
  
第三條,無序。

  返回的結果往往是很隨意,各種錯誤資訊本來就是用Http的狀態碼構成的,可是很多人還是喜歡把錯誤資訊返回在返回值中。最常見的就是Code和Message,當然對於這一點,我個人是保留疑問的,我的觀點是,Http本身的錯誤和伺服器的內部錯誤還是需要在不斷層面分開的,不能混在一起。可是在大神眼裡並非如此。

那麼怎麼解決這些問題呢? 

強迫症患者的福音就是先頒規則第一個規則就是明確Url是什麼,該怎麼用。就是所有的Url本質

來講,都應該是一種資源。一個獨立的Url地址,就是對應一個獨一無二的資源。怎麼樣?這種感覺是不是棒棒噠?一個冰淇淋,一個老師,一間房子,在Url上對應的都是一個資源,不會有多餘的Url跟他對應,也不會表示有多個Url地址 
  注意,這裡點的是Url地址,並不是單獨的引數,他就是一個/room/{room_id}這樣的東西,舉個栗子,/room/3242 這就表示3242號房間。這是一個清爽的世界啊,你想想,之前的Url是什麼都要,我開房,可能是/open/room/3242 我要退房可能是/exit/3242/room,我要打理房間,可能是room/3242?method=clean.夠了!這些亂七八糟的東西全夠了,讓世界迴歸清爽的本質,一間房,就是/room/3242 沒有別的Url地址了。
  在過去的混亂世界裡,經常用的就是Get和Post。如果不是因為Get不支援大資料傳輸,我想連Post都不會有人使用。(想像一下Roy Fielding在憤怒的對著電腦螢幕喊,Http的Method一共有八個,你們為毛只逮著Get一隻羊的毛薅薅薅薅薅)。

  而對資源最常見的操作是什麼?CRUD,對不對,就是建立,讀,更新,刪除。再看Http的Method?是不是非常完美?其實也怪Fielding老爺子一開始命名不準確,如果剛開始就是把Get方法叫做ReadPut方法叫做UpdatePost叫做Create這該多好。。。
  你用一個Get,大家又發現沒什麼限制沒什麼所謂,又很難理解Put和Post的差別,法無禁止即可為啊,呃,老爺子不要瞪我,我瞎說的。總之,這四種方法夠不夠你浪?你有本身找出來更多的對資源的操作來啊,我還有4個Method沒用過呢。如果這4個真的不夠了,有什麼問題,大不了我再重新更改http協議啊。其實簡單說,對於Rest理解到這裡就夠了。後續的東西,都是在這一條基礎上空想出來的,比強迫症更強迫症,當然,無狀態我是百分百支援的。以上的各種表述可能不太準確,也純屬是我的意淫和各種小道資料,並未考據,但是憑良心講,我是早就看不慣黑暗年代裡的Url命名風格了,所以當時最早接觸到Rest的時候,瞬間就找到了真愛,我靠,這不就是我一直想要的答案嗎?但是我一直想的僅僅是命名規範,從來沒有把自己的思考角度放在一個url就是一個資源,所有的操作都是對資源的更改而言的角度上啊。所以你能理解到的程度,更多的就是在於你要弄清楚你要解決的什麼問題,如果你的問題只是理解Rest,恐怕你很理解,如果你的問題是怎麼解決Url混亂的問題,你反而很快能弄懂了~

對比

https://localhost:8080/myweb/getDogs --> GET /rest/api/dogs 獲取所有小狗狗 
https://localhost:8080/myweb/addDogs --> POST /rest/api/dogs 新增一個小狗狗 
https://localhost:8080/myweb/updateDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一個小狗狗 
https://localhost:8080/myweb/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 刪除一個小狗狗
操作成功 或者 1

或者

操作失敗 或者 0

這還要我們自己去解析,還要前端和後端去協商你返回的0是啥意識啊。但是REST返回值是標準的,比如

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: xxx

{
   "url" : "/api/categories/1",
   "label" : "Food",
   "items_url" : "/api/items?category=1",
   "brands" : [
         {
            "label" : "友臣",
            "brand_key" : "32073",
            "url" : "/api/brands/32073"
         }, {
            "label" : "樂事",
            "brand_key" : "56632",
            "url" : "/api/brands/56632"
         }
         ...
   ]
}

格式固定,第一行永遠是操作失敗或者成功的狀態碼,第二行是返回的型別,第三行內容的長度,第五行開始是內容。

這樣我只需寫一個程式解析返回的資訊就可以了,可以重用,但是我們上面傳統的不僅僅要協商,還有有不同的解析程式,稍微改變,就不能正常使用了。所以rest的明顯更加通用。

列子2

1、獲取文章

請求:
GET /blog/post/{postId} HTTP/1.1

響應:
HTTP/1.1 200 OK
{
    "title": "foobar",
    "content": "foobar",
    "comments": ["", "", ""]
}

2、釋出文章

請求:
POST /blog/post HTTP/1.1
{
    "title": "foobar",
    "content": "foobar",
    "comments": ["", "", ""]
}

響應:
HTTP/1.1 201 CREATED

規則

GET    用來獲取資源,
POST  用來新建資源(也可以用於更新資源),
PUT    用來更新資源,
DELETE  用來刪除資源

例子

DELETE http://api.qc.com/v1/friends: 刪除某人的好友 (在http parameter指定好友id)
POST http://api.qc.com/v1/friends: 新增好友UPDATE 
http://api.qc.com/v1/profile: 更新個人資料

概念

REST 是面向資源的,這個概念非常重要,而資源是通過 URI 進行暴露。
URI 的設計只要負責把資源通過合理方式暴露出來就可以了。對資源的操作與它無關,操作是通過 HTTP動詞來體現,所以REST 通過 URI 暴露資源時,會強調不要在 URI 中出現動詞。

比如:左邊是錯誤的設計,而右邊是正確的

GET /rest/api/getDogs --> GET /rest/api/dogs 獲取所有小狗狗 
GET /rest/api/addDogs --> POST /rest/api/dogs 新增一個小狗狗 
GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一個小狗狗 
GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 刪除一個小狗狗

REST很好地利用了HTTP本身就有的一些特徵,如HTTP動詞、HTTP狀態碼、HTTP報頭等等
REST API 是基於 HTTP的,所以你的API應該去使用 HTTP的一些標準。這樣所有的HTTP客戶端(如瀏覽器)才能夠直接理解你的API(當然還有其他好處,如利於快取等等)。REST 實際上也非常強調應該利用好 HTTP本來就有的特徵,而不是隻把 HTTP當成一個傳輸層這麼簡單了。

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: xxx

{
   "url" : "/api/categories/1",
   "label" : "Food",
   "items_url" : "/api/items?category=1",
   "brands" : [
         {
            "label" : "友臣",
            "brand_key" : "32073",
            "url" : "/api/brands/32073"
         }, {
            "label" : "樂事",
            "brand_key" : "56632",
            "url" : "/api/brands/56632"
         }
         ...
   ]
}

看這個響應,包含了http裡面的狀態碼等資訊。還會有http的一些報頭。

Authorization 認證報頭 
Cache-Control 快取報頭 
Cnotent-Type  訊息體型別報頭 
......

REST 系統的特徵

  1. 客戶-伺服器(Client-Server),提供服務的伺服器和使用服務的客戶需要被隔離對待。
  2. 無狀態(Stateless)來自客戶的每一個請求必須包含伺服器處理該請求所需的所有資訊。換句話說,伺服器端不能儲存來自某個客戶的某個請求中的資訊,並在該客戶的其他請求中使用。
  3. 可快取(Cachable),伺服器必須讓客戶知道請求是否可以被快取。(Ross:更詳細解釋請參考 理解本真的REST架構風格 以及 StackOverflow 的這個問題 中對快取的解釋。)
  4. 分層系統(Layered System),伺服器和客戶之間的通訊必須被這樣標準化:允許伺服器和客戶之間的中間層(Ross:代理,閘道器等)可以代替伺服器對客戶的請求進行迴應,而且這些對客戶來說不需要特別支援。
  5. 統一介面(Uniform Interface),客戶和伺服器之間通訊的方法必須是統一化的。(Ross:GET,POST,PUT.DELETE, etc)
  6. 支援按需程式碼(Code-On-Demand,可選),伺服器可以提供一些程式碼或者指令碼(Ross:Javascrpt,flash,etc)並在客戶的執行環境中執行。這條準則是這些準則中唯一不必必須滿足的一條。(Ross:比如客戶可以在客戶端下載指令碼生成密碼訪問伺服器。)

詳細解釋

無狀態(Stateless)

   所謂無狀態的,即所有的資源,都可以通過URI定位,而且這個定位與其他資源無關,也不會因為其他資源的變化而改變。有狀態和無狀態的區別,舉個簡單的例子說明一下。如查詢員工的工資,如果查詢工資是需要登入系統,進入查詢工資的頁面,執行相關操作後,獲取工資的多少,則這種情況是有狀態的,因為查詢工資的每一步操作都依賴於前一步操作,只要前置操作不成功,後續操作就無法執行;如果輸入一個url即可得到指定員工的工資,則這種情況是無狀態的,因為獲取工資不依賴於其他資源或狀態,且這種情況下,員工工資是一個資源,由一個url與之對應,可以通過HTTP中的GET方法得到資源,這是典型的RESTful風格。

這裡寫圖片描述 

這裡寫圖片描述 

統一介面(Uniform Interface)

  RESTful架構風格規定,資料的元操作,即CRUD(create, read, update和delete,即資料的增刪查改)操作,分別對應於HTTP方法:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源,這樣就統一了資料操作的介面,僅通過HTTP方法,就可以完成對資料的所有增刪查改工作。

即:

GET(SELECT):從伺服器取出資源(一項或多項)。
POST(CREATE):在伺服器新建一個資源。
PUT(UPDATE):在伺服器更新資源(客戶端提供完整資源資料)。
PATCH(UPDATE):在伺服器更新資源(客戶端提供需要修改的資源資料)。
DELETEDELETE):從伺服器刪除資源。

演化

優點&缺點

優點是因為他對uri進行了限制,只用於定義資源。這樣看起來比較容易理解。尤其是對簡單的物件的增刪改查,很好理解。

缺點是因為這種限制,導致設計uri變得複雜了。尤其是複雜的關係,操作,資源集合,硬性套用rest原則設計非常困難。在rest基礎上的HATEOAS,返回的json裡增加了相應的關係和url。這也同樣帶來問題。好處是對簡單的關係,的確可以通過url進一步處理。但對複雜的關係和操作,HATEOAS並不能勝任描述。反而在單純的資料中增加了一堆垃圾資訊。

是什麼?

REST是一個標準,一種規範,遵循REST風格可以使開發的介面通用,便於呼叫者理解介面的作用。