【REST WCF】30分鐘理論到實踐

先來點理論知識,來自 http://www.cnblogs.com/simonchen/articles/2220838.htmlhtml

一.什麼是Rest


  REST軟件架構是由Roy Thomas Fielding博士2000年在他的論文《Architectural Styles and the Design of Network- based Software Architectures》首次提出的。他提出的理論對後來的Web技術的發展產生了巨大的影響,他是許多重要Web架構標準的設計者,這些標準就是 HTTP、URI等。web

  • Rest的英文全稱是「Representational State Transfer」。中文翻譯爲「表述性狀態轉移」。REST自己只是爲分佈式超媒體系統設計的一種架構風格,而不是標準。
  • 那麼如何理解「Representational State Transfer」這句話呢?下面咱們來解釋一下:
    1.     Representational :中文直譯:表明的,表像的。若是把WEB 服務器端中全部的東西(數據)都看做是資源(Resource),那麼呈如今用戶面前(客戶端)的就是資源的表像(Representation)。每個資源都有本身的惟一標識(URI)。
    2.     State :中文直譯:狀態。首先這個狀態是客戶端的狀態,而不是服務器端的狀態(在REST 中,服務器端應該是無狀態的)。那麼,把State和Representation聯繫在一塊兒(Representational State),能夠理解成:每個資源(Resource)在客戶端的表像(Representation)就是客戶端的一個狀態(State)。
    3.     Transfer:中文直譯:轉移。當用戶經過不一樣的URI訪問不一樣的資源時,客戶端的表像(Representation)也會隨着變化,也就意味着客戶端的狀態變動(Transfer)了,連起來就是:Representational State Transfer。
  • REST=老的Web規範+3個新的規範:REST實際上也是基於已有的Web規範集合產生的。傳統的Web應用大都是BS系統,這些系統共同遵循一些老的Web規範,這些規範主要包含 3條:
    1.   客戶-服務器:這種規範的提出,改善了用戶接口跨多個平臺的可移植性,而且經過簡化服務器組件,改善了系統的可伸縮性。最爲關鍵的是經過分離用戶接口和數據存儲這兩個關注點,使得不一樣用戶終端享受相同數據成爲了可能。
    2.   無狀態性:無狀態性是在客戶-服務器約束的基礎上添加的又一層規範。他要求通訊必須在本質上是無狀態的,即從客戶到服務器的每一個request都 必須包含理解該request所必須的全部信息。這個規範改善了系統的可見性(無狀態性使得客戶端和服務器端沒必要保存對方的詳細信息,服務器只須要處理當 前request,而沒必要了解全部的request歷史),可靠性(無狀態性減小了服務器從局部錯誤中恢復的任務量),可伸縮性(無狀態性使得服務器端可 以很容易的釋放資源,由於服務器端沒必要在多個request中保存狀態)。同時,這種規範的缺點也是顯而易見得,因爲不能將狀態數據保存在服務器上的共享 上下文中,所以增長了在一系列request中發送重複數據的開銷,嚴重的下降了效率。
    3.       緩存:爲了改善無狀態性帶來的網絡的低效性,咱們填加了緩存約束。緩存約束容許隱式或顯式地標記一個response中的數據,這樣就賦予了客戶 端緩存response數據的功能,這樣就能夠爲之後的request共用緩存的數據,部分或所有的消除一部分交互,增長了網絡的效率。可是用於客戶端緩存了信息,也就同時增長了客戶端與服務器數據不一致的可能,從而下降了可靠性。

 

  • REST在原有的架構上增長了3個新規範:統一接口、分層系統和按需代碼:
    1.       統一接口:REST架構風格的核心特徵就是強調組件之間有一個統一的接口,這表如今REST世界裏,網絡上全部的事物都被抽象爲資源,而REST 就是經過通用的連接器接口對資源進行操做。這樣設計的好處是保證系統提供的服務都是解耦的,極大的簡化了系統,從而改善了系統的交互性和可重用性。而且 REST針對Web的常見狀況作了優化,使得REST接口被設計爲能夠高效的轉移大粒度的超媒體數據,這也就致使了REST接口對其它的架構並非最優的。
    2.       分層系統:分層系統規則的加入提升了各類層次之間的獨立性,爲整個系統的複雜性設置了邊界,經過封裝遺留的服務,使新的服務器免受遺留客戶端的影響,這也就提升了系統的可伸縮性。
    3.       按需代碼:REST容許對客戶端功能進行擴展。好比,經過下載並執行 applet或腳本形式的代碼,來擴展客戶端功能。但這在改善系統可擴展性的同時,也下降了可見性。因此它只是REST的一個可選的約束。

二.Rest的特色


     因爲Rest遵照的這些規範,所以Rest架構的特色也很是的明顯:json

  •   REST是一種架構,而不是一個規範。
  •   REST是一種典型的Client-Server架構,可是強調瘦服務器端,服務器端只應該處理跟數據有關的操做,全部有關顯示的工做都應該放在客戶端。
  •   在REST架構中,服務器是無狀態的,也就是說服務器不會保存任何與客戶端的會話狀態信息。全部的狀態信息只能放在雙方溝通的 Message(消息)中。
  •   REST架構是冪等的,對於相同的請求,服務器返回的結果也是相同的,所以服務器端返回的結果是能夠緩存的,既能夠存在客戶端也能夠存在代理服務器端。
  •   在REST架構中,全部的操做都是基於統一的方式進行的:
    1.     每一個Resource都有一個惟一的ID。
    2.     經過Representation(客戶端)來處理Resource(服務器端)。也就是說,客戶端不能直接操做服務器端的Resource,只能經過對相應的Representation的操做,併發送相應的請求,最後由服務器端來處理Resource並返回結果。
    3.     客戶端和服務器端傳送的任何一個Message(消息),都應該是自描述的。也就是說處理這個 Message所須要的上下文環境都應該包含在這個Message當中。
    4.     多媒體的交互系統,客戶端和服務器端傳送的內容能夠是文檔,圖片,聲音等等多媒體數據,這也是一個Resource可以對應不一樣的Representation(例如文檔,圖片等)的基礎。 

 

  •   分層結構,像TCP/IP的分層結構同樣,第n層使用第n-1層提供的服務併爲第n+1層提供服務。在REST中,Client- Server之間加入了Proxy層和Gateway層。在這些中間層能夠加入一些業務處理之外的功能,譬如:負載均衡,安全控制等等。
  •   Code-On-Demand,客戶端能夠訪問服務器端的Resource,但並不知道如何處理服務器端返回的結果,這個處理過程的代碼應該是從服務器端發送過來,而後在客戶端執行,也就是說客戶端的功能是根據須要動態從服務器端得到的。一個很簡單的例子,Applet就是從服務器端下載而後在客戶端執行的。注意,這個特性是可選的(Optional),也就是說在你的REST實現當中,能夠不考慮這個特性。

三.Rest的優勢


      既然Rest風格有這些特色,那麼也就具有了許多優勢:瀏覽器

  •   緩存使用 HTTP 向 RESTful 端點申請數據時,用到的 HTTP 動詞是 GET。對於 GET 請求響應中返回的資源,能夠用多種不一樣的方式進行緩存。Conditional GET 就是可供選擇的一種實現細節,客戶端能夠向服務驗證他的數據是否爲最新版本;RESTful 端點能夠經過它進一步提升速度和可伸縮性。
  •   擴展 REST 鼓勵每項資源包含處理特殊請求所需的全部必要狀態。知足這一約束時,RESTful 服務更易於擴展且能夠沒有狀態。
  •   反作用如您使用 GET 請求資源,RESTful 服務應該沒有反作用(遺憾的是,與其餘一些 REST 約束相比,這一約束更容易被打破)。
  •   冪等統一接口另外兩個經常使用到的主要 HTTP 動詞是 PUT 和 DELETE。用戶代理想要修改資源時最常使用 PUT,DELETE 能夠自我描述。要點(也就是「冪等」一詞所強調的)是您能夠對特殊資源屢次使用這兩個動詞,效果與首次使用同樣——至少不會有任何其餘影響。構建可靠的分佈式系統時(即錯誤、網絡故障或延遲可能致使屢次執行代碼),這一優勢可提供保障。
  •   互操做性許多人將 SOAP 捧爲創建客戶端-服務器程序最具互操做性的方法。但一些語言和環境至今仍沒有 SOAP 工具包。有一些雖然有工具包,但採用的是舊標準,不能保證與使用更新標準的工具包可靠溝通。對於大多數操做,REST 僅要求有 HTTP 庫(固然,XML 庫一般也頗有幫助),它的互操做性確定強過任何 RCP 技術(包括 SOAP)。
  •   簡易性與其餘優勢相比,這一優勢更主觀一些,不一樣的人可能有不一樣的感覺。對我而言,使用 REST 的簡易性涉及到表明資源的 URI 和統一接口。做爲一名 Web 衝浪高手,我理解在瀏覽器中輸入不一樣的 URI 能夠獲得不一樣的資源(有時也被稱爲 URI 或 URL 黑客,但絕無惡意)。因爲有多年使用 URI 的經驗,因此爲資源設計 URI 對我來講駕輕就熟。使用統一接口簡化了開發過程,由於我沒必要爲每一個須要創建的服務構建接口、約定或 API。接口(客戶端與個人服務交互的方式)由體系結構約束設置。

四.Rest的設計原則


  REST架構是針對Web應用而設計的,其目的是爲了下降開發的複雜性,提升系統的可伸縮性。REST提出了以下設計準則:緩存

  1. 網絡上的全部事物都被抽象爲資源(resource),好比圖片、音樂、視頻、文字、以及服務等等;
  2. 每一個資源有惟一的資源標識符(resource identifier),URI定位資源;
  3. 經過通用的鏈接器接口(generic connector interface)對資源進行操做,好比使用 HTTP 標準動詞(GET、POST、PUT 和 DELETE)的統一接口完成操做;
  4. 對資源的各類操做不會改變資源標識符,URI不變;
  5. 全部的操做都是無狀態的(stateless)。

五.wcf3.5到wcf4.0 Rest的新增特性

 

  1. 增長對路由的支持
  2. 對緩存的支持。
  3. 幫助(help)頁面的改進。
  4. 消息錯誤處理
  5. 消息格式的多樣性如(XML\JSON\ATOM\TEXT\BINARY)
  6. 簡化操做。

甩過一遍理論,那麼就趁熱實踐一番吧!安全

六.實踐出真知

 

按正常步驟新建一個WCF應用,常見的CRUD操做服務器

 [ServiceContract]
    public interface IExampleService
    {

        [OperationContract]
        string GetData(string value);

        [OperationContract]
        string AddData(string value);

        [OperationContract ]
        string UpdateData(string value);

        [OperationContract ]
        string DeleteData(string value);

    }

那麼rest模式該是如何呢?網絡

 [ServiceContract]
    public interface IExampleService
    {

        [OperationContract]
        [WebGet(UriTemplate = "/Rest/Get/{value}", ResponseFormat = WebMessageFormat.Json)]
        string GetData(string value);

        [OperationContract]
        [WebInvoke(UriTemplate = "/Rest/Add/{value}", Method = "POST")]
        string AddData(string value);

        [OperationContract ]
        [WebInvoke(UriTemplate = "/Rest/Update/{value}", Method = "PUT")]
        string UpdateData(string value);

        [OperationContract ]
        [WebInvoke (UriTemplate="/Rest/Delete/{value}",Method="DELETE")]
        string DeleteData(string value); 
       
    }

比較下就很容易看出多加了些標籤,而且也從方法的使用上能夠對應出GET、POST、PUT、DELETE的使用。架構

wcf能夠看元數據,那麼rest也有對應的方式,在web.config中添加以下配置就能夠查看help頁面併發

 <services>
      <service name="RestWCFTest.ExampleService">
        <endpoint address="" behaviorConfiguration="HelpBehavior" binding="webHttpBinding"
          bindingConfiguration="" contract="RestWCFTest.IExampleService" />
      </service>
    </services>

  <behaviors>
      <endpointBehaviors>
        <behavior name="HelpBehavior">
          <webHttp helpEnabled="true" />
        </behavior>
      </endpointBehaviors>
</behaviors>

help頁面以下

點擊方法進去能夠看見調用方式

咱們的接口實現

   public string GetData(string value)
        {
            return string.Format("You entered: {0}", value);
        }

        public string AddData(string value)
        {
            return string.Format("You added: {0}", value);
        }

        public string UpdateData(string value)
        {
            return string.Format("You updated: {0}", value);
        }

        public string DeleteData(string value)
        {
            return string.Format("You deleted: {0}", value);
        }

如今咱們用fiddler來模擬請求測試下

在composer選項裏有模擬請求的功能,very good!咱們先來調用GetData操做,根據參數獲取數據,根據設置的URI模板,「123456」爲匹配

的參數,執行它!

看請求的信息

GET http://localhost/REST4/ExampleService.svc/Rest/Get/123456 HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 0

看響應的數據

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 21
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 05:16:52 GMT

"You entered: 123456"

200 OK 調用正常,content-type是json,由於咱們指定的,IIS是7.5,對,個人確是部署在7.5上。。。看結果也是和預期如出一轍,so easy~

可能有同窗會問,這是返回的json數據麼?我也以爲不是,若是在方法標籤上修改成以下

 [OperationContract]
        [WebGet(UriTemplate = "/Rest/Get/{value}",BodyStyle=WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]
        string GetData(string value);

多加了個修飾bodystyle,它的功能是對結果進行包裝,包裝後再看返回的結果

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 39
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 06:34:24 GMT

{"GetDataResult":"You entered: 123456"}

果真,被包裝了,它是一個json格式的數據了。

POST

請求

POST http://localhost/REST4/ExampleService.svc/Rest/Add/1234 HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 0

響應

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 92
Content-Type: application/xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 05:06:41 GMT

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You added: 1234</string>

這個時候咱們沒有指定返回的格式,默認爲XML。

PUT

請求

PUT http://localhost/REST4/ExampleService.svc/Rest/Update/123 HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 0

響應

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 93
Content-Type: application/xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 05:23:04 GMT

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You updated: 123</string>

DELETE

請求

DELETE http://localhost/REST4/ExampleService.svc/Rest/Delete/123 HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 0

響應

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 93
Content-Type: application/xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 05:14:56 GMT

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You deleted: 123</string>

有同窗可能DELETE請求發出去沒反應(IIS 7.5),請在web.config里加上如下節點

 <system.webServer>
<modules runAllManagedModulesForAllRequests="true">
      <remove name="WebDAVModule" />
    </modules>
<handlers>
      <remove name="WebDAV" />
    </handlers>
 </system.webServer>

至此通常的傳參狀況就是如此了,下面列舉一些其它傳參狀況

   [OperationContract]
        [WebGet(UriTemplate = "/Rest/GetList2/", ResponseFormat = WebMessageFormat.Json)]
        List<ExampleData> GetDataLs2();

   [OperationContract]
        [WebInvoke(UriTemplate = "/Rest/AddLs3", BodyStyle = WebMessageBodyStyle.Wrapped,
            ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, Method = "POST")]
        List<ExampleData> AddDataLs3(List<ExampleData> datas);

        [OperationContract]
        [WebInvoke(UriTemplate = "/Rest/AddLs4", BodyStyle = WebMessageBodyStyle.Wrapped,
            ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, Method = "POST")]
        List<ExampleData> AddDataLs4(List<ExampleData> datas1, List<ExampleData> datas2);

實體

  public class ExampleData
    {
        public string Name
        {
            get;
            set;
        }

        public string Age
        {
            get;
            set;
        }
    }

接口實現

  public List<ExampleData> GetDataLs2()
        {
            List<ExampleData> result = new List<ExampleData> { 
                new ExampleData{ Name="張三", Age="20"}
                ,new ExampleData {Name="李四",Age="21"}
                 ,new ExampleData {Name="王五",Age="30"}
            };

            return result;
        }

  public List<ExampleData> AddDataLs3(List<ExampleData> datas)
        {
            return datas;
        }

  public List<ExampleData> AddDataLs4(List<ExampleData> datas1, List<ExampleData> datas2)
        {
            List<ExampleData> result = new List<ExampleData>();
            result.AddRange(datas1);
            result.AddRange(datas2);

            return result;
        }

 

咱們看到有獲取實體集合了,還有傳參的時候也是實體集合了

首先看看獲取集合的狀況

請求

GET http://localhost/REST4/ExampleService.svc/Rest/GetList2/ HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 0

響應

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 88
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 05:21:52 GMT

[{"Age":"20","Name":"張三"},{"Age":"21","Name":"李四"},{"Age":"30","Name":"王五"}]

嗯,返回的格式不錯。

看看怎樣作新增操做的

AddDataLs3

請求

POST http://localhost/REST4/ExampleService.svc/Rest/AddLs3 HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 41

{"datas":[{"Name":"xiaohua","Age":"13"}]}

這時候咱們會注意到,多了request body了,而且datas就是參數名

響應

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 52
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 06:59:55 GMT

{"AddDataLs3Result":[{"Age":"13","Name":"xiaohua"}]}

被包裝了的數據。

AddDataLs4

請求

POST http://localhost/REST4/ExampleService.svc/Rest/AddLs4 HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 78

{"datas1":[{"Name":"xiaohua","Age":"13"}],"datas2":[{"Name":"li","Age":"13"}]}

響應

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 77
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2013 07:02:58 GMT

{"AddDataLs4Result":[{"Age":"13","Name":"xiaohua"},{"Age":"13","Name":"li"}]}

 

面對茫茫多的CRUD的時候,咱們也許會顯得不耐煩,由於每一個操做都去寫方法,真是煩躁,不妨能夠試下以下的方式

   [OperationContract]
        [WebInvoke(UriTemplate = "/Rest/*", Method = "*", ResponseFormat = WebMessageFormat.Json)]
        string ExecuteData();

用星號來匹配全部的請求,讓程序區識別請求究竟是GET、POST、PUT仍是DELETE

實現

   public string ExecuteData()
        {
            var request = WebOperationContext.Current.IncomingRequest;

            var method = request.Method;
            var args = request.UriTemplateMatch.WildcardPathSegments;

            switch (method)
            {
                case "POST":
                    return "POST...";
                case "DELETE":
                    return "DELETE...";
                case "PUT":
                    return "UPDATE...";
                default:
                    return "GET...";
            }
        }

嗯,不知不覺就貼了這麼多代碼了,其實我字沒寫多少,今天就到這吧,算是入門了吧。

以上例子全部源碼下載

相關文章
相關標籤/搜索