本文章是由機器翻譯。

table.one { table-layout:fixed;width: 600px; } .tdwidth1{ width:150px; } .tdwidth2{ width:150px; } tr.heading td { padding : 10px; background-color:white; color: black; vertical-align:top; } tr.fig td { background-color:black; color: white; padding: 10px; font-weight:bold; vertical-align:top; } tr.dark td { background-color: #b9d1dc; color: black; padding: 10px; vertical-align:top; } tr.lite td { background-color: #dde8ee; color: black; padding: 10px; vertical-align:top; } tr.small td { background-color: #dde8ee; color: black; padding: 5px 10px 5px 10px; vertical-align:top; font-size: 10px; }

服務站

RESTful 服務(配備 WCF)介紹

Jon Flanders

代碼下載位置:MSDN 代碼庫
線上流覽代碼

目錄

瞭解 REST
抽象示例
您為什麼應關注 REST?
WCF 和 REST
WebGetAttribute 和 WebInvokeAttribute
UriTemplate 和 UriTemplateTable
WebHttpBinding 和 WebHttpBehavior
WebServiceHost 和 WebServiceHostFactory
使用示例代碼

這是 Windows® Communication Foundation (WCF) Web 服務構建專欄系列的第一篇文章,該服務使用稱為具象狀態傳輸 (Representational State Transfer, REST) 的體系結構風格。 您可能說在服務站專欄中,2009 年將是 REST 的天下,我認為這已經是遲到了許久的看法。 自 2000 年面市後,REST 已在市場上受寵多年。

在本系列的第一個專欄中,我將討論 REST 的一些基本原則,並演示使用 WCF 實現 RESTful 服務。 在後續專欄中,我將深入探討 REST 基本理念的更多細節,以及使用這一基礎構建的技術。

瞭解 REST

體系結構風格是一組可在執行構建時應用的約束。 軟體的體系結構風格會對功能做出說明,這些功能用於指導建立軟體系統。 REST 這一體系結構風格所構建的軟體供用戶端(使用者代理)請求服務(端點)之用。 REST 是實現用戶端/伺服器體系結構風格的一種途徑——REST 實際上是明確構建在用戶端/伺服器體系結構風格之上的。

REST 這一術語最早是由 Roy Thomas Fielding 在他的博士論文(“體系結構風格和基於網路軟體體系的設計”)中提出的。 他當時參與制訂的規範對於今天的互聯網起到了巨大的推進作用,這個規範就是:超文字傳輸協定 (HTTP)。 一般來講,體系結構風格描述人的背景與風格討論並無干系,但這一觀點在此並不適用,因為我認為能對 REST 產生基本瞭解的最佳途徑之一是思索 Web 及其工作方式。

我必須假定您作為開發人員對 Web 很熟悉,或者可能象我一樣,每天都使用 Web 或對其癡迷。 事實證明,Web 在任何時候皆可視為最大、伸縮性最強且最流行的分散式應用程式。 REST 的約束採用的就是掌控 Web 的基本原則。 這些原則是:

  • 使用者代理與資源交互,任何可命名和表達的事物都可稱為資源。 每項資源都有一個唯一的統一資源識別項 (URI)。
  • 與資源的交互(通過其唯一的 URI 定位)使用 HTTP 標準動詞(GET、POST、PUT 和 DELETE)的統一介面完成。 交互中聲明資源的媒體類型也很重要,它使用 HTTP 內容類別型標頭指定。 (XHTML、XML、JPG、PNG 和 JSON 就是一些廣為人知的媒體類型。 )
  • 資源是自我描述的。 處理資源請求所需的全部資訊均包含在請求本身內(這樣服務可以沒有狀態)。
  • 資源包含到其他資源的連結(超媒體)。

抽象示例

使用 REST 體系結構風格的服務通常稱為 RESTful 服務或端點。 為簡單感受一下此體系結構風格的內部理念,我將提供一個小示例。 假定我需要構建一項服務,它要使用《MSDN 雜誌》的相關資料——該服務可以告訴我《MSDN 雜誌》所有的發表年份和每期中的每篇文章。 對它的要求是雜誌的編輯可以使用它新增文章並管理即將發表期刊的資料。

構建 RESTful 服務時,您可通過完成一組非常簡單的基本步驟來設計自己的服務。

  1. 您需要什麼樣的資源?
  2. 將使用哪些 URI 表示這些資源?
  3. 每個 URI 將支援統一介面的哪些部件(HTTP 動詞)?

以下是 RESTful 端點的基本構建塊:資源及其表示、這些資源的 URI 及每個 URI 回應統一介面的部件。 還有您可使用的更高級功能,例如更直接地使用狀態碼及使用超連結管理資源狀態,但本示例僅討論基礎內容。

現在,我可以使用這些步驟設計我的假想服務。 資源是《MSDN 雜誌》所有的發表年份、每年發表的所有期刊及每期雜誌中發表的所有文章。 出於對此特殊示例目的的考慮,我將使用媒體類型應用程式/xml (XML) 表示這些資源,但請注意,RESTful 的媒體類型不僅限於 XML。

接下來,我需要為每項資源確定 URI。 現在,我僅需確定相對 URI,絕對 URI 將由端點的託管位置確定。 年份清單將是服務的根 URI (/)。 使用此語法,/{year} 將返回每年的所有期刊;/{year}/{issue} 是每期的 URI(我按出版月份標識每期);/{year}/{issue}/{article} 代表每篇文章(我假定每期中的文章編號從 1 到 n)。

然後將 URI 映射到統一介面。 由於雜誌的歷史記錄實際上應該是唯讀狀態,因此根資源將僅顯示 GET。 可以通過對 URI /{year} 執行 PUT 加入新的年份。 PUT 用於在用戶端知曉新資源的 URI 後新建資源,如本例所示。 PUT 還能在知曉 URI 後更新現有資源。 POST 用於在用戶端不知曉新資源 URI 的情況下創建資源,因此在新增文章資源時會使用 POST,新文章將發給 /{year}/{issue} URI。

我可以繼續講述每項資源和每個動詞,但希望您已對確定 RESTful 端點設計的步驟有了大致的瞭解。 圖 1 中顯示了資源、URI 和動詞的完整清單。

圖 1 作業系統支援
資源 URI 動詞
所有年份 "/ " GET
特定年份的期刊 "/{year}" GET、PUT
特定期刊 "/{year}/{issue}" GET、PUT
文章 "/{year}/{issue}/{article}" GET、POST(由系統指定文章編號),PUT、DELETE(期刊發表後將關閉 Delete)

如果我想以用戶端的身份獲取 2006 年 1 月 的文章,我將對 /2006/January 執行 HTTP GET。 如果我是編輯,想向 2008 年 12 月刊新增文章,用戶端將把文章資源 POST 給 /2008/December,這樣就能將新文章加入此期刊。 通過反復使用這種模式,我就能以用戶端的身份使用此服務。

您為什麼應關注 REST?

現在我對 REST 做一些解釋,您可能正在想為什麼要對它加以關注。 作為開發人員,您需要一定的動機來學習和採納任何風格、技術或模式。 如果您閱讀此雜誌,您可能是常與 Microsoft 技術打交道的開發人員。 要實現用戶端-伺服器應用程式,您可能使用的是另一種體系結構風格:遠端程序呼叫 (RPC)。 無論您用過的是專用 RPC 系統(例如 DCOM 或 .NET Remoting),還是可交互操作的 RPC 技術(例如,使用 ASMX 或 WCF 的 SOAP),它們都是在 Microsoft 平臺上展現的用戶端-伺服器風格。 那麼,為什麼要學習或使用 REST?

據我所知有兩個主要原因。 首先,REST 在許多方面所表現出的重要功能和優點都遠勝於 RPC 技術。 其次,Microsoft 正在將自身許多實現從 RPC 技術(例如 SOAP)轉為 REST。 這意味著即使您對使用 REST 構建自己的系統尚在猶豫,隨著 Microsoft 和其他公司將更多的框架和技術轉向 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 是唯一一種經常使用的技術。 您應該瞭解的是它的長處,以便在需要時能夠加以利用。

WCF 和 REST

WCF 是 Microsoft 的框架,用於構建使用網路通信的應用程式,無論其風格和協定為何。 WCF 是要創建一個可擴展且可嵌入的框架,這樣,開發人員就能學習一種程式設計與配置模型,將這些技能應用於多種不同的分散式系統。

雖然 WCF 的主要作用是配合 RPC(使用 SOAP),但是由於最初是做為 .NET Framework 3.0 的一個部件發佈的,它的確可以公開並使用 REST 服務。 它所缺乏的是能輕鬆搭配使用 REST 和 WCF 的程式設計模型。 要將 REST 與 .NET Framework 3.0 配合使用,您還必須構建基礎架構的一些部件。 在 .NET Framework 3.5 中,WCF 在 System.ServiceModel.Web 元件中新增了程式設計模型和這些基礎架構部件。 .NET Framework 3.5 SP1 還多了幾項小改進。

程式設計模型有兩個主要的新屬性:WebGetAttribute 和 WebInvokeAttribute;還有一個 URI 範本機制,説明您聲明每種方法回應用的 URI 和動詞。 基礎架構的組成是一個綁定 (WebHttpBinding) 和一種行為 (WebHttpBehavior),為使用 REST 提供適宜的連網堆疊。 此外,自訂的 ServiceHost (WebServiceHost) 和 ServiceHostFactory (WebServiceHostFactory) 還提供了一些託管基礎架構方面的説明。

WebGetAttribute 和 WebInvokeAttribute

WCF 簡化連接系統構建的一種方法是:將網路資訊路由給類實例的方法(您將其定義為服務的實現)。 這使您可以將精力集中于服務中的代碼邏輯上,而不必在意處理網路流量所需的基礎結構。

WCF 預設根據操作的概念執行此路由(也稱為調度)。 為了執行此調度,需要在 WCF 代表您接收的每條消息內包含一個操作。 每個唯一的操作均映射為一種特殊的 Action 方法。

Action 的值基於方法的名稱(加上您服務的命名空間)或自訂值(通過 OperationContractAttribute.Action 屬性設置)。 由於預設使用 SOAP 規範的 Action 標頭,所以此路由系統與 SOAP 結合得非常緊密。 幸運的是,就象 WCF 中的絕大部內容一樣,這一預設的調度基礎架構也是可替換的。

將 REST 基礎結構與 WCF 搭配使用時,會替換預設的調度程式,新程式不是以 Action 為基礎,而是以傳入請求的 URI 和所使用的 HTTP 動詞為基礎。 這一路由(由 WebHttpDispatchOperationSelector 類完成)讓您可以輕鬆實現 RESTful 端點。 此調度程式由稱為 WebHttpBehavior 的行為在每個端點上進行配置,您必須將該行為添加到要使用此程式設計模型的所有端點(稍後您會看到,不必經常手動完成這項工作)。

這項工作主要是為讓 WebHttpDispatchOperationSelector 瞭解如何將各個 URI 和動詞映射為您的方法。 為此,必須將 WebGetAttribute 和 WebInvokeAttribute 添加至您 WCF ServiceContract 類型的方法中。

WebGetAttribute 告訴調度程式方法應該回應 HTTP GET 請求。 WebInvokeAttribute 預設映射為 HTTP POST,但可將 WebInvokeAttribute.Method 屬性設置為支援所有其他 HTTP 動詞(PUT 和 DELETE 是最常用的兩個)。 URI 預設由方法的名稱確定(添加到端點的基礎 URI 之上)。

實際上,這不是非常鮮明的 RESTful,由於方法名稱表示為動詞,所以它們還不是您想在 REST 中處理的條目。 您是希望將 URI 公開為名詞。 為此,WCF REST 程式設計模型允許為每個方法自訂 URI,具體操作是使用可在 WebGetAttribute 或 WebInvokeAttribute 上通過 UriTemplate 屬性設置的範本。

UriTemplate 和 UriTemplateTable

為能針對每個方法和動片語合自訂 URI,WCF 新增了為每種資源定義 URI 的功能,它使用特殊的範本語法,例如先前我在本專欄中說明《MSDN 雜誌》服務端點所用的語法。 您可以使用此語法和可替換的權杖定義您需要的 URI 結構,即結合 HTTP 動詞表示每個方法(通過 WebGetAttribute 或 WebInvokeAttribute)。 我將在後續專欄中探討該語法的更多細節。

圖 2 顯示了《MSDN 雜誌》服務的 WCF ServiceContract 定義(使用適合的屬性和 UriTemplate 自訂)並應用了我在先前所述的功能。 它使用 WebGetAttribute 為那些應回應 GET 的操作擴展了現有的 WCF 約定定義系統。 並向操作中添加了 WebInvokeAttribute 以回應任何其他動詞。

圖 2 WCF ServiceContract 定義

[ServiceContract]
public interface IMSDNMagazineService
{
    [OperationContract]
    [WebGet(UriTemplate="/")]
    IssuesCollection GetAllIssues();
    [OperationContract]
    [WebGet(UriTemplate = "/{year}")]
    IssuesData GetIssuesByYear(string year);
    [OperationContract]
    [WebGet(UriTemplate = "/{year}/{issue}")]
    Articles GetIssue(string year, string issue);
    [OperationContract]
    [WebGet(UriTemplate = "/{year}/{issue}/{article}")]
    Article GetArticle(string year, string issue, string article);
    [OperationContract]
    [WebInvoke(UriTemplate = "/{year}/{issue}",Method="POST")]
    Article AddArticle(string year, string issue, Article article);

}

在本例中,我在 AddArticle 方法內添加了 Method="POST" 來提高可讀性,因為 WebInvokeAttribute 的預設動詞是 POST。 GET 和 POST 方法均有使用 UriTemplate 屬性的 URI 自訂。 請注意,UriTemplate 語法允許有多個變數路徑段,每段均做為參數傳遞給方法。

WebHttpBinding 和 WebHttpBehavior

在 WCF 中,由綁定確定 WCF 的通信方式。 實際上是由綁定這一配置告訴 WCF 如何構建通道堆疊,該堆疊是一組協同工作的物件,用於提供您特殊端點所需的通信。

對於 RESTful 端點,您使用的綁定是 WebHttpBinding。 與其他多數綁定不同,WebHttpBinding 相當簡單,只包含兩個元件:HTTP 傳輸和文本消息編碼器(設置為不期望或使用 SOAP,只有純 XML)。

如前所述,WebHttpBehavior 是造成使用“URI+動詞”調度程式的成因物件,因此常常會組合使用 WebHttpBinding 和 WebHttpBehavior。 以下是您自託管 WCF RESTful 端點時創建此類端點的代碼:

ServiceHost sh = 
  new ServiceHost(typeof(MSDNMagazineServiceType));
string baseUri = "http://localhost/MagazineService";
ServiceEndpoint se = 
  sh.AddServiceEndpoint(typeof(IMSDNMagazineService),
  new WebHttpBinding(), baseUri);
se.Behaviors.Add(new WebHttpBehavior());
sh.Open();

請注意,向 ServiceHost 添加端點時,我不但必須要指定 WebHttpBinding,還必須明確向端點添加 WebHttpBehavior,以便啟用“URI+動詞”調度系統。 當然,我也可以利用配置完成這一操作(請參見圖 3)。

圖 3 配置中的 URI+動詞調度

<configuration>
  <system.serviceModel>
    <services>
      <service name="MSDNMagazine.MSDNMagazineServiceType">
        <endpoint 
          address="http://localhost/MagazineService" 
          binding="webHttpBinding" 
          contract="MSDNMagazine.IMSDNMagazineService" 
          behaviorConfiguration="webby"/>
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="webby">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

WebServiceHost 和 WebServiceHostFactory

對 WCF 的一個抱怨是有時過於複雜,特別是在配置方面。 為了使 RESTful 端點能夠在這方面有所改進(再次重申,簡易性是 REST 反復宣揚的優點),Microsoft 向 .NET Framework 3.5 中新增了兩種類型:WebServiceHost 和 WebServiceHostFactory。

WebServiceHost 是源自 ServiceHost 的類型,它簡化了 RESTful 端點的自託管環境。 WebServiceHost 的自託管代碼如下所示:

string baseUri = "http://localhost/MagazineService";
WebServiceHost sh = 
  new WebServiceHost(typeof(MSDNMagazineServiceType),
  new Uri(baseUri));
sh.Open();

這是一個相當不錯的優化,它沒有手動添加 WebHttpBinding 和 WebHttpBehavior 的重複代碼。 WebServiceHost 類自動創建端點並使用 WebHttpBinding 和 WebHttpBehavior 對其進行配置。

在涉及 WCF 的受控託管環境中,在 Internet 資訊服務 (IIS) 的內部,WCF 通常需要指向服務類型的 .svc 檔,以及 web.config 檔中的條目,以便向 WCF 通知端點的情況(其他配置中的綁定和行為)。

為簡化受控託管環境,Microsoft 增加了 WebServiceHostFactory,它在受控託管環境中使用開源 WCF 擴展點(採用自訂的 ServiceHostFactory 類型),為諸多 RESTful 服務創建無須配置的體驗。 .svc 檔類似如下所示:

<%@ ServiceHost Factory=
  "System.ServiceModel.Activation.WebServiceHostFactory"   
  Service="MSDNMagazine.MSDNMagazineServiceType" %>

WebServiceHostFactory 創建一個 WebServiceHost 的實例,由於 WebServiceHost 將使用 WebHttpBinding 和 WebHttpBehavior 自動配置端點,因此根本無須在 web.config 中對此端點做任何配置。 (當然,如果需要自訂綁定,則必須使用配置系統或構建源自 WebServiceHost/WebServiceFactory 的類)。 如果我確實需要自訂綁定,我仍可在設定檔中添加合適的條目。 圖 4 顯示了將在我的服務端點上開啟 HTTP 基本驗證的設定檔。

圖 4 開啟 HTTP 基本驗證

<configuration>
<system.serviceModel>
  <services>
    <service name="MSDNMagazine.MSDNMagazineServiceType">
      <endpoint 
        address="http://localhost/MagazineService" 
        binding="webHttpBinding" 
        contract="MSDNMagazine.IMSDNMagazineService" 
        behaviorConfiguration="webby"/>
    </service>
  </services>
  <bindings>
    <webHttpBinding>
      <binding name="secure">
        <security mode="Transport">
          <transport clientCredentialType="Basic"/>
        </security>
      </binding>
    </webHttpBinding>
  </bindings>
  <behaviors>
    <endpointBehaviors>
      <behavior name="webby">
        <webHttp/>
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>
</configuration>

使用示例代碼

通過聯繫 REST 說明 WCF 的功能,我已展示了在本專欄開頭所提及的服務實現的主要內容。 接下來介紹如何與此服務交互。

啟動並運行服務後,我可利用任何用戶端訪問其根 URI,包括 Internet Explorer 這樣的 Web 流覽器。 許多人都同意,之所以能夠使用流覽器快速檢驗 RESTful 端點,要歸功於以 Web 工作方式為基礎的 REST 體系結構風格。 結果如圖 5 所示。

fig05.gif

圖 5 根資源 URI

在本例中,我在 Visual Studio 2008 Web Development Server 中執行託管,它給出了基本 URI。 Issues.svc 檔是受控託管環境中 WCF 的必備檔。 如果我想看到特定年份的結果,我只需將它添加到流覽器的位址 (URI) 中即可(請參見圖 6)。

fig06.gif

圖 6 表示 2007 年的資源

如果我請求 2008 年 10 月,URI 是 localhost:1355/Issues.svc/2008/October,此時它是一個空集。 如果我想添加文章,我會對該 URI 執行 HTTP POST,將文章的 XML 表示做為 HTTP 請求的主體。

HTTP 另一個相當出色的功能是所有可用工具都能與其交互。 我喜歡使用Fiddler,它讓我能“監視”HTTP 請求和回應。 還能讓我隨意執行 HTTP 操作。 因此,我可以使用 Fiddler Request Builder 選項卡執行 HTTP POST(請參見圖 7)。 在 POST 後,您可看到對 2008 年 10 月資源的請求,如圖 8 所示。

fig07.gif

圖 7 使用 Fiddler 執行 HTTP POST 請求以新建文章資源

fig08.gif

圖 8 新增的文章資源

雖然對 Internet Explorer 和 Fiddler 是否為真正的用戶端仍有爭議,但以我剛剛完成的這些簡單步驟為基礎,即可構建實際的用戶端-伺服器實現(後續專欄中會介紹構建 RESTful 用戶端更為複雜的示例)。 用戶端需要瞭解每項資源的 URI,以及對每個 URI. 使用統一介面的哪些部件。 用戶端可以隨後使用這些資訊構建自己的功能,與服務進行交互。

我在這一首期專欄仲介紹的是 WCF 的一組基本功能,通過它們可以在您的 .NET 應用程式中輕鬆使用 REST 體系結構風格。 這一基礎能帶來其他有趣的技術,包括 Web 源(RSS 和 Atom)以及支援用 JSON 編碼的資源與 AJAX 交互。

在接下來的幾期專欄中,我將鞏固 REST 和 WCF 的這一基礎知識,並深入探討 Microsoft 平臺(建立在此風格和技術上)中的幾種其他功能。 我還將處理更多 REST 的常見問題,例如,安全性和實現處理端點方面的問題。

請將您想詢問的問題和提出的意見發送至sstation@microsoft.com

Jon Flanders 是 Pluralsight 的獨立顧問、發言人和培訓師。 他專攻 BizTalk Server、Windows Workflow Foundation 和 Windows Communication Foundation。 您可以通過masteringbiztalk.com/blogs/jon與 Jon 聯繫。