RestAPI的實現

轉自:http://blog.csdn.net/yanical/article/details/7856670html

Rest的做者認爲計算機發展到如今,最大的成就不是企業應用,而是web,是漫漫無邊的互聯網web世界。Web能有這麼大的成就,它值得咱們研究。因此Rest的做者仔細研究了Web,按照Web的世界一些關鍵特性,提出了咱們在實現企業應用的時候應該遵循的一種風格,就是Restful。java

Rest風格的API能夠給咱們不少好處,好比:簡潔,統一,性能,可擴展性等等。惋惜的是,在實現Rest的時候,總有一些Rest的關鍵特性沒有實現,好比,無狀態性,這在我作過的兩個項目和我知道的另一個項目都存在。事實上要實現無狀態性在Java裏不是那麼容易,由於那意味着要把servlet的session拋棄了。除此以外,Rest的一些其餘特性在各個項目中實現的也是各有不一樣。android

接下來,我會列出一些我認爲的,要實現Rest風格API的關鍵步驟:web

 

1. 全部東西都是資源(Resource)

全部要給API操做的對象都只能是資源。無論實際上存在的,仍是抽象上的。全部資源都會有一個不變的標識(ID),對資源的任何API操做都不該該改變資源的標識。資源和其餘資源會有關係,資源與資源的關係經過資源的標識來引用。對資源的操做都應該是完整的,好比獲取資源拿到的應該是一個完整的資源對象(根據企業引用特色有些例外,後面會提到)。json

事實上,上面的這些完徹底全是按照互聯網的特性提出來的。互聯網中,一個URL就是一個資源;資源的內容就是HTML頁面;無論怎麼改HTML內容,URL都不會改變;資源之間經過HTML裏的鏈接聯繫起來;每次獲取的時候,獲取到的都是完整的HTML內容。瀏覽器

假設有一個博客系統,那麼其中的資源能夠包括:博主,每一個博主都是一個資源;博客,每篇博客都是一個資源,博客和博主之間有聯繫,經過ID聯繫起來;每篇博客都會有回覆,回覆也算是資源,可是它是隸屬於博客的,能夠認爲回覆是博客的子資源(你也能夠認爲博客是博主的子資源,怎麼抽象取決於具體的應用,這裏不討論)。緩存

這步一般不難實現,由於這和ORM中的對象概念是相似的,實現上,若是用了hibernate之類的框架,改動也應該很小。安全

 

2. 規範對資源的操做,最好只包括CRUD

CRUD指建立(Create),讀取(Read), 更新(Update),刪除(Delete)服務器

一般對資源的操做只包含CRUD是不可能的,CRUD裏甚至連查找的操做的都沒有。但這不妨礙咱們對Rest的理解,Rest提出的要求是,對資源的操做都應該是統一的,不論是操做哪一種類型的資源,API都應該是一致的。這樣當調用API的客戶端知道某種資源的時候,它不須要去學習對這個資源該怎麼操做,由於對全部資源的操做都是一致的,它們都應該支持CRUD操做,以及一些其餘操做,好比list(用來查找,或者列出全部資源), merge(部分更新資源,這應該是惟一的不操做資源全部內容的API)。session

這和Web也是同樣的,HTTP裏只有GET,PUT,POST,HEAD等等幾個統一的請求(參考:http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)。

要實現簡單的幾個操做不難,難在這幾個簡單操做無法支撐整個系統的需求。可是想一想吧,互聯網也夠複雜了吧,仍是不是成功了,並且魚和熊掌不可兼得。有時候服務器端也不必定要實現全部東西,能夠把一些邏輯交給客戶端去作。好比顯示,Rest裏顯示是徹底交給客戶端去處理的,服務器只管數據的存儲,無論客戶端是網頁,仍是一個手機應用程序,仍是另一個企業應用。各類各樣的客戶端進來,他們會有各類各樣的需求,服務器端不可能一一知足,只能客戶端使用統一的API去組合,實現本身的需求。

 

3. 規範URL的使用

好了,對資源的操做統一了,可是客戶端仍是要知道怎麼觸發對資源的某個具體的操做。Rest用URL的規範來保證這種統一性。

建立並保存一個博客:

 

[plain]  view plain  copy
 
  1. POST /blog/save  


這個請求須要返回博客的保存後的結果,其中包括博客的標識(ID)。 獲取一個已經保存的博客,並更新它:

 

 

 

[plain]  view plain  copy
 
  1. GET /blog/get/345  
  2. //更新它  
  3. POST /blog/update/345  

 

這個博客的標識是345。獲取博客的某個回覆:

 

[plain]  view plain  copy
 
  1. GET /blog/get/345/reply/456  

 

對待子資源,一般的作法就是和這個例子同樣,是一級一級的往下找。咱們還能夠有其餘方法,好比remove用來刪除,merge用來部分更新,list用來查找。

有三種方式能夠將參數傳給API操做:

 

第一種是經過URL的地址傳遞,如前面的例子中把標識放在URL裏;

第二種是經過URL的參數,好比,對於一個查找請求,能夠把查找的過濾條件放在參數裏:

 

[plain]  view plain  copy
 
  1. GET /blog/list?name=Azure:用InstanceInputEndpoint直接和指定instance通訊  

第三種是PUT或者POST請求的時候,把內容放在HTTP body裏面。這裏一般就是博客的內容。

 

前面咱們的例子中有些請求是GET,有些是POST,其實這是有原則的。一般對資源內容沒有改變的操做都實用GET,好比獲取資源,查找資源;對資源有改變的操做都用POST,好比保存資源。

若是想作的更好,咱們應該近一步的使用HTTP的請求方法,直接把HTTP方法和要作的操做映射起來。好比我喜歡認爲GET請求就是獲取資源(get),PUT方法就是更新整個內容(save,update,我以爲這兩個不必區分),POST方法就是更新部份內容(merge),DELETE方法就是刪除資源(remove)。若是這樣的話,請求的URL又能簡化:

 

[plain]  view plain  copy
 
  1. PUT   /blog           //建立保存一個新的博客  
  2. GET   /blog/345    //獲取博客345內容  
  3. PUT   /blog/345    //更新博客345  
  4. GET   /blog/345/reply/456     //獲取博客345的回覆456  
  5. POST /blog/345    //更新博客345的部份內容  
  6. DELETE /blog/345   //刪除博客345  


固然對於list操做,這裏就無法知足了,仍是須要在URL層面上作些區別。

 

對於merge操做,有不少人認爲是沒必要要的,Rest不該該提供這個API,可是我以爲在某些狀況下頗有用。好比某個資源對象,它的內容在不斷的擴充,怎麼讓老的客戶端在內容擴充後還能繼續使用呢? 若是咱們要求全部更新請求都必須把全部內容都放在請求的body中,對於客戶端來講就不是那麼好作了,可是若是咱們容許merge請求,客戶端能夠能夠徹底忽略新增長的字段,而只把本身知道的字段放在請求內容中便可。

4. 資源的多重表述

這一步我以爲不是必須的。

Rest裏,資源的內容一般直接做爲一段JSON或者XML返回給客戶端。資源多重表述指的是,一個資源應該可以支持根據客戶端的請求,返回相應的格式給客戶端。服務器應該按照請求HTTP頭中的Accept屬性決定返回格式。好比對於Ajax請求,Accept頭是application/json,服務端返回JSON格式;對於Android請求,Accept頭是application/xhtml+xml,服務器返回XML格式。

我以爲這一步不是必須的由於至少從項目前期來講,咱們應該都只會支持一種格式。資源的多重表述給咱們一種處理多重請求格式的方式,可是咱們不須要一開始就支持它。

 

5. 進一步合理利用HTTP

前面咱們已經應用了HTTP的一些東西,好比請求方法,Accept頭。事實上咱們能夠利用更多。

HTTP支持客戶端緩存,在HTTP響應裏利用Cache-Control,Expires,Last-Modified三個頭字段,咱們可讓瀏覽器緩存資源一段時間。Rest也能夠利用這些頭,告訴客戶端在必定時間內不須要再次請求資源。這對提升性能有很大好處。更多HTTP頭信息,能夠參考http://en.wikipedia.org/wiki/List_of_HTTP_header_fields

Rest的請求會出錯,HTTP的請求也會出錯。咱們能夠直接利用HTTP的response code來告訴客戶端Rest請求出了什麼錯誤。好比500,告訴客戶端,服務器出錯了;401告訴客戶端須要把安全驗證信息附上,須要登陸系統;404告訴請求的資源不存在,等等。更多HTTP響應碼,能夠參考http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html。在實際的業務中,HTTP的那些response code確定是不能知足全部需求的,適當的在response body中加上更詳細的錯誤信息也是必須的。

還有其餘不少,總之能利用上的就利用上,不比再次發明輪子。

 

6. 實現請求的無狀態

Rest是無狀態的。Rest的請求之間不該該有依賴,在調用一個請求前,不須要必定要去提早調用另一個請求。Rest裏面不該該有session,特別是Rest請求不該該保存信息在sesssion裏,以便在後面的調用中使用。甚至包括安全驗證,客戶端不該該須要提早登陸,而後把權限信息保存在session裏,後面的請求用同一個session來調用。

實現無狀態的方法就是,把全部信息都包含在當前的請求中,包括驗證信息。HTTP是無狀態的,HTTP裏有一個Authorization頭,HTTP的要求是在每次請求的時候都把驗證信息放在裏面,服務器每次處理請求前都去驗證這個信息。爲了安全,咱們能夠提供一個生成token的Rest API,客戶端調用這個API生成token(能夠附上用戶名/密碼來生成token)。在後面的全部請求中都把這個token放在Authentication頭中。

實現無狀態最大的好處是可以方便的擴展服務器,也即scalability。不然的話,咱們要麼把Session綁定到具體服務器上,要麼用一個共享的空間存儲Session。而實現無狀態後,咱們能夠隨意增長,減小服務器數量,都不會對當前用戶形成影響。

相關文章
相關標籤/搜索