本文章是由機器翻譯。

服務站

建置 RESTful 用戶端

Jon Flanders

下載範例程式碼

在這一篇的 RESTful 服務站我將討論 RESTful 服務建置用戶端。建立用戶端察覺困難 (多半是因為缺乏來自中繼資料,à 拉自動用戶端產生 SOAP 和 WSDL),但實際上這就像是任何其他類型的撰寫的程式碼:第一次有是某些斜道時間您習慣以特定的程式設計範例。一旦您有擱置的該範例,撰寫對它的程式碼就更容易更容易。我發現這是撰寫用戶端對 RESTful 的服務則為 True,只要有許多我互動其他開發人員。

用戶端的基本概念

我要顯示在用戶端和服務使用 REST 之間互動的基本概念。只是一個快速重點 REST 之前我們挖中有關。REST 是建立在同一個原則,提供網際網路的基礎的架構樣式。用戶端與服務互動藉由 HTTP 的要求,並以通常包含的用戶端程式碼可以使用磁碟機的應用程式的資源表示的 HTTP 回應的服務回應]。

假設我有的服務,公開資訊] 和 [Hyper-V (內建 Windows Server 2008 虛擬化技術) 與相關的功能。如果 「 項目"這項服務的 URI 是資源的 http://localhost/HyperVServices/VMs.svc/,我進行的 HTTP GET 要求取得由這個 URI 所識別表示用戶端。在這種情況下資源設定為 XML 格式,並代表安裝在特定的電腦上的所有虛擬機器 (VM) 子清單。


圖 1 的簡單的 HTTP GET 要求

為我顯示在第一個有關 REST,2009 年 1 月發行的 MSDN Magazine (msdn.microsoft.com/magazine/2009.01.servicestation.aspx),在我服務站文件中使用的小型的優點之一 REST 是您可以使用 HTTP 要求的工具,讓您對服務的初始測試要求。(使用這些工具來偵錯問題是也很有用)。在這種情況下我使用免費工具,稱為 Fiddler (fiddlertool.com) 發出,表示我所有的虛擬機器資源的 HTTP GET 要求。請參閱的圖 1]。就說 Fiddler 等工具是有用的但是您必須撰寫對服務的程式碼若要建置的應用程式。我要開始製作的 HTTP 要求,然後再進入實際讀取資源的問題的基本概念。

.NET Framework 永遠有提供基本的 HTTP API 可以用來與 RESTful 服務互動。這個 API 的中心是 HttpWebRequest 和 HttpWebResponse 型別。若要將 HTTP 要求您可以建立和設定 HttpWebRequest 執行個體,然後詢問 [HttpWebResponse。[圖 2] 顯示範例。提出要求到 RESTful 服務是簡單的設定步驟:

  1. 請確定您使用正確的 URI。
  2. 請確定您使用正確的方法 (HTTP 動詞命令)。
  3. 如果狀態的程式碼是 200 確定,請處理回應。
  4. 處理與其他的狀態碼為適當 (多在此之後)。

因為很簡單步驟 1 和 2,一般來說步驟 3 (而且有時是步驟 4) 最後會更困難的步驟。

大部分的步驟 3 的工作正在處理資源表示以某種合理的方式。由於 XML 仍是最常見的資源回應,我將在本文中討論的。(其他可能的媒體類型會是 JSON)。當資源 XML,您必須剖析該資源,並撰寫程式碼,可以從該 resource.Using.NET 擷取所需的資料,有幾個選項可用來剖析 XML。沒有嘗試與真正的 XmlReader 類別。XmlDocument 類別也是一個的可能性,藉由使用這些型別其中之一可以手動剖析或使用 XPath 瀏覽方式將 XML。以.NET 3.0 的問世,也進入 [XDocument 類別的項目,與 LINQ to XML 組合時, 已經成為 de facto 處理 XML 的選擇。圖 3 顯示使用 XDocument 處理的 VM 清單的程式碼範例。

圖 2的簡單 HttpWebRequest 程式碼

string uri = "http://localhost/HyperVServices/VMs.svc/";
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
//this is the default method/verb, but it's here for clarity
webRequest.Method = "GET";
var webResponse = (HttpWebResponse)webRequest.GetResponse();
Console.WriteLine("Response returned with status code of 0}",
webResponse.StatusCode);
if (webResponse.StatusCode == HttpStatusCode.OK)
ProcessOKResponse(webResponse);
else
ProcessNotOKResponse(webResponse);

LINQ to XML,匿名型別,一起提供好且容易的方式,來處理將 XML 轉換成物件的應用程式可以處理,並且您也可以代替匿名型別使用預先定義的型別的就說。

會導致回應自動還原序列化成.NET 型別程式設計,以 SOAP 為基礎和以 REST 為基礎的服務時,是常用的另一個方法。在 SOAP 的情況下這通常發生在 WSDL 產生 Proxy 中。與 Windows Communication Foundation (WCF) REST,這可以被完成幾個方法中。(這我真的不建議,但我一提的完整性) 一種方法是使用對稱 WCF 的本質,要在用戶端使用 WCF 服務合約定義。在就其實 REST WCF 支援包含一個名為 WebChannelFactory,可用來建立用戶端通道,對服務合約定義的型別。建議不要此類型的原因有兩個程式的用戶端。第一次,建立用戶端變得非常手動、 容易發生錯誤。第二個,使用強型別的服務合約建立緊密的聯繫,您的用戶端與服務之間。避免過度緊密的聯繫性是其中一個主要原因,網站已成功,我們想要以程式設計方式使用網站時,請繼續的趨勢。

若要使用 XML 序列化的另一個方法是使用 HttpWebResponse.GetResponseStream 方法,並以手動方式將 XML 還原序列化至物件。您可以只要使用 [XmlSerializer 或 WCF DataContract 序列化程式。在大部分的情況下,XmlSerializer 是較佳的方法,因為比 DataContract 序列化程式不會處理 XML 文件 (比方就說 [屬性] 和 [非限定的項目]) 中的多個變化。

圖 3 處理 RESTful 回應,使用 XDocument

var stream = webResponse.GetResponseStream();
var xr = XmlReader.Create(stream);
var xdoc = XDocument.Load(xr);
var vms = from v in xdoc.Root.Elements("VM")
select new { Name = v.Element("Name").Value};
foreach (var item in vms)
{
Console.WriteLine(item.Name);
}

這個問題仍然看起來圓形回到事實 RESTful 服務通常不公開中繼資料可以用來自動產生部份或全部這段程式碼。雖然許多 RESTful 營地中看不到此問題 (一次,任何服務定義可以被視為邪惡的代理程式的緊密結合的 autogenerates),但當然有時也會不需要這些工具時要採用 REST 構成妨礙。

WCF REST 入門套件 (asp.net/downloads/starter-kits/wcf-rest/),也就是從 Microsoft 的程式設計模型的.NET 3.5,[REST 加強所採用的一個有趣方法是提供部分 autogeneration 功能。如果您安裝入門套件,您必須新增功能表項目在 Visual Studio 2008 中在 [編輯] 功能表底下如您所見 圖 4


圖 4 貼上 XML 為 的型別功能表項目

這個命令的使用模式就很簡單。您將複製到剪貼簿 XML 資源表示 (從一些人們可讀取的文件服務公開或藉由使用像是 Fiddler 工具要求)。當您執行這項操作建立一組 XmlSerializable 型別是,可讓您再將 [HttpWebResponse 從資料流到物件。請參閱 圖 5 (產生的型別主體不保持簡潔的起見,顯示)。

圖 5 使用貼上為 的型別 XML 從產生的程式碼

var stream = webResponse.GetResponseStream();
//in a real app you'd want to cache this object
var xs = new XmlSerializer(typeof(VMs));
var vms = xs.Deserialize(stream) as VMs;
foreach (VMsVM vm in vms.VM)
{
Console.WriteLine(vm.Name);
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "",
IsNullable = false)]
public partial class VMs
{
private VMsVM[] vmField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("VM")]
public VMsVM[] VM
{
get
{
return this.vmField;
}
set
{
this.vmField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.4918")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class VMsVM
{
//omitted
}

「 REST 入門套件 」 不只可以簡化 XmlSerializer,使用,但是它也提供了一個很好的 API HttpWebRequest/WebResponse API 的頂端。下列程式碼重新撰寫使用 REST 入門套件的 HttpClient API 的圖 1,顯示簡單的 GET 要求:

 

string uri = "http://localhost/HyperVServices/VMs.svc/";
var client = new HttpClient();
var message = client.Get(uri);
ProcessOKResponse(message.Content.ReadAsStream());

HttpClient 類別大幅簡化了 (和明確讓您使用的何種動詞命令).NET HTTP API 的使用。 而且,您可以看到下列的程式碼是為了簡化貼上 XML 」 類型產生功能的使用:

var vms = XmlSerializerContent.ReadAsXmlSerializable<VMs>(
message.Content);

雖然 REST 入門套件不正式支援 Microsoft 還,它不會說明很重要的一點:程式設計的 REST 用戶端可以簡化,並且部分不需要完整的中繼資料 à 拉 RESTful 服務自動化 WSDL 檔案。

使用 HTTP

一種方法用戶端和服務可以利用 HTTP 是正常使用狀態碼和標頭。在 w3.org/Protocols/rfc2616/rfc2616-sec10.html 找狀態碼的相關資訊。

如我已經消失說話 REST 有關在世界各地,其中一個通常點出的 RESTful 服務的優點是可以快取 GET 要求。請沒有發生錯誤擴充性,並成功的 Web 是主要是因為 HTTP 快取。

其中一個方法要利用 HTTP 快取是使用條件式 GET。條件式 GET 啟用用戶端 (「 使用者代理程式 」在 HTTP 術語) 讓使用者代理程式已有一份資源的 GET 要求。如果資源尚未變更,伺服器告訴使用者代理程式資源會完全相同為已經保留的使用者代理程式的版本。條件式 GET 效率優點是減少網路伺服器和釋放頻寬,要用於要求的新建立或修改資源將使用者代理程式之間的頻寬使用量。在就另外它會節省額外的處理時間序列化所需之的資源但不處理時間,產生或擷取資源 (因為您需要一份與使用條件式 GET 使用者代理程式所傳送的資訊相比較,目前的資源)。

圖 6 伺服器端執行的條件式 GET

[OperationContract]
[WebGet(UriTemplate = "/{name}")]
public VMData GetOne(string name)
{
VMManager.Connect();
var v = VMManager.GetVirtualMachine(name);
var newVM = FromVM(v);
string etag = GenerateETag(newVM);
if (CheckETag(etag))
return null;
if (newVM == null)
{
OutgoingWebResponseContext ctx =
WebOperationContext.Current.OutgoingResponse;
ctx.SetStatusAsNotFound();
ctx.SuppressEntityBody = true;
}
SetETag(etag);
return newVM;
}
private bool CheckETag(string currentETag)
{
IncomingWebRequestContext ctx =
WebOperationContext.Current.IncomingRequest;
string incomingEtag =
ctx.Headers[HttpRequestHeader.IfNoneMatch];
if (incomingEtag != null)
{
if (currentETag == incomingEtag)
{
SetNotModified();
return true;
}
}
return false;
}
string GenerateETag(VMData vm)
{
byte[] bytes = Encoding.UTF8.GetBytes(vm.ID +
vm.LastChanged.ToString());
byte[] hash = MD5.Create().ComputeHash(bytes);
string etag = Convert.ToBase64String(hash);
return string.Format("\"{0}\"", etag);
}
void SetETag(string etag)
{
OutgoingWebResponseContext ctx =
WebOperationContext.Current.OutgoingResponse;
ctx.ETag = etag;
}

就像大部分的其他 「 進階 」HTTP 的概念 WCF 不支援條件式 GET 自動因為條件式 GET 的實作是高度變數之間服務實作。但是,與其他 「 進階 」HTTP 的概念 WCF 提供工具來執行條件式 GET。有兩個方法,若要完成這項作業:使用上次修改資源,時間,或使用所謂的 ETag 唯一識別項。

ETags 變得要實作條件 GET,更受歡迎的方法。的圖 6,則可以看到來實作一個 ETag 為基礎的條件式 GET VM 在服務上的程式碼。請注意這是伺服器端實作 ;我會給用戶端的。

基本的流程是尋找在的用戶端傳送如果-無-符合 HTTP 標頭中 ETag,並嘗試使其符合針對目前的 ETag 產生資源的伺服器。在這種情況下使用每個 VM 以及上次修改時間戳記 (轉換成位元組,然後所做的是很常見的實作 MD5 雜湊成) 的唯一識別碼。如果兩個值相符,伺服器會傳回一個 304 不修改的 HTTP 標頭與儲存序列化時間,以及頻寬的空白內文。用戶端取得更快速的回應,並知道它可以使用它已經有相同的資源。


圖 7 的一個簡單的 WPF VM 用戶端]

假設一個用戶端應用程式,如 [圖 7.中顯示這個應用程式顯示的每個 VM,名稱加上目前的 VM 狀態的影像。現在假設您想要更新此應用程式,以符合每個 VM 的目前狀態。若要完成這個工作,您必須撰寫程式碼中一個計時器並更新每一筆記錄,是否不同於您的本機記錄。而且很可能您可以更新所有項目只是為了簡化的應用程式的每個反覆項目時,但這是資源的浪費。

如果您而是在用戶端上使用條件式 GET,您可以輪詢變更服務藉由傳送條件式 GET 要求,可以使用一個 If-無-符合 HTTP 標頭,表示資源的 [ETag。針對 VM 集合,依序服務可以用來產生 ETag 的最最近變更的 VM 和用戶端會更新只有一個或多個 VM 已變更其狀態]。(如果您有大量 VM 時,您可能想要這的每個 VM。但因為我們是資料繫結至集合,此應用程式中,[確定] 以更新整個集合)。

現在,實作此一邏輯不太過很困難,但它是其中一個 REST 入門套件會讓您實作的功能。包含在程式碼範例中是檔案具有自動的條件式 GET 用戶端的投票 PollingAgent.cs RESTful 的端點上命名為您所定義的間隔。當 [PollingAgent 決定該資源已變更 (因為服務不再傳回一個 302) 時, 它就會引發回呼。

因此在我簡單的 WPF 應用程式,我只需要回撥 (也就是 HttpResponseMessage 物件) 的結果,我為新資料的控制項重新繫結。[圖 8] 顯示程式碼以實作使用 [PollingAgent 這個應用程式。

下列的超文字

離開的用戶端主旨前, 我想要介紹另一個重要的條件約束的 REST:您可以使用 hypermedia 為應用程式狀態 (稱為 HATEOAS) 引擎。

我發現 HATEOAS 是容易瞭解的人類的 Web 內容中。使用 [人力的網頁時有時我們必須書籤我們知道我們想要造訪的網站。這些的站台內然而,我們通常依照根據我們的需求,一天中的超連結。我可能會有 www.amazon.com,為書籤,但隨意購買東西我依照每一頁 (資源),若要新增至我的車的項目連結。然後再我依照超連結 (和在表單) 在處理我的順序每個後續頁面上。在每個階段訂單處理頁面中的連結代表目前的應用程式狀態 (亦即可以做什麼?,作為使用者代理程式,)。

一個重要的考量時 (雖然的部分就是目前在 RESTful 服務實作器上) 建置 RESTful 的用戶端保留"書籤 」在最小值。這表示幾乎是用戶端應該具有以最小知識您的表示法的 URI 結構,但盡可能他們應該知道如何 「 瀏覽""超連結 」在那些資源。我將 「 超連結 」引號括住因為任何一項您的資源中的資料可能會相對 URI 建立下一個 「 連結 」用戶端。

圖 8 條件式 GET 使用輪詢代理程式 REST 入門套件

public Window1()
{
InitializeComponent();
string uri = "http://localhost/HyperVServices/VMs.svc/";
var client = new HttpClient();
var message = client.Get(uri);
var vms = XmlSerializerContent.ReadAsXmlSerializable<VMs>(
message.Content);
_vmList.DataContext = vms.VM;
var pa = new PollingAgent();
pa.HttpClient = client;
pa.ResourceChanged += new EventHandler<ConditionalGetEventArgs>(
pa_ResourceChanged);
pa.PollingInterval = new TimeSpan(0, 0, 5);
pa.StartPolling(new Uri(uri), message.Headers.ETag, null);
}
void pa_ResourceChanged(object sender, ConditionalGetEventArgs e)
{
var vms = XmlSerializerContent.ReadAsXmlSerializable<VMs>(
e.Response.Content);
_vmList.DataContext = vms.VM;
}

在我的範例中每個 VM 的名稱確實是一個的連結因為用戶端可以要求特定 VM 的資料,所以名稱要求 URI 的一部分。所以 http://localhost/HyperVServices/VMs.svc/MyDesktop (其中 MyDesktop 是名稱項目在集合內的 VM 的項目中的值) 是 MyDesktop VM 資源的 URI。

想像一下此 RESTful 服務用於佈建,並啟動 VM。它會使可以置於 VM 在特定的時間,而不是讓用戶端啟動 VM,沒有已提供正確但內嵌的各種狀態的每個 VM 資源超連結內的有意義。

使用 HATEOAS 有助於驗證之的應用程式的目前狀態,並且它也鼓勵更鬆散聯繫的用戶端 (因為用戶端不需要知道儘量有關服務的 URI 結構)。

比您想更容易

沒有更難建立 RESTful 的用戶端要這樣做比建立 SOAP 為基礎的用戶端支援的 WSDL 中繼資料是確定。我寫了我的前一個資料行中,「 REST 很簡單,但簡單並不一定表示簡單。SOAP 是容易 (因為 WSDL) 但簡單不一定表示簡單 」 有良好的編碼方式] 和 [像 REST 入門套件的工具],建置 RESTful 用戶端可以比您想更容易。然後,最後我認為您會發現您收到來自使用架構的樣式的優點將多個組成的任何暫時性的延遲建立用戶端。

Jon Flanders* 是獨立的顧問、 喇叭和 Pluralsight 的訓練。他會指定在 BizTalk Server]、 [Windows 工作流程基礎] 和 [Windows 通訊基礎。您可以在 masteringbiztalk.com/blogs/jon 他連絡。*