非常 ASP.NET |
使用 AJAX Extensions 用戶端進行 Web 服務調用 |
|
|
Fritz Onion |
|
列印 |
|
E-mail |
|
添加到收藏夾 |
|
評價 |
|
RSS (非常 ASP.NET) |
|
Add RSS to Any |
|
相關資訊 |
|
Live Spaces |
|
Digg This |
|
BlogThis! |
|
Slashdot |
|
del.icio.us |
|
Technorati |
|
Explore the code. Download the code. |
|
Get the sample code for this article.
目錄使用 AJAX 調用 Web 服務
NEW: Explore the sample code online! - or - 代碼下載位置: ExtremeASPNET2007_01.exe (160KB) |
工作原理 序列化 總結 |
從根本上講,ASP.NET 自始至終都是一項伺服器端技術。當然,在某些情況下 ASP.NET 會產生用戶端 JavaScript,特別是在驗證控制項中以及在新推出的 Web 組件基礎結構中,但它通常只是簡單地將用戶端屬性轉換成用戶端行為。作為開發人員,在收到下一個 POST 請求之前不必考慮與用戶端進行互動。對於需要使用用戶端 JavaScript 和 DHTML 構建更具互動性的頁面的開發人員而言,則需要在 ASP.NET 2.0 指令碼回調功能提供的一些協助下自己編寫代碼。這一情況在去年得到了徹底改變。 在 2005 年 9 月的 Microsoft 在 Microsoft 專業開發人員大會上發布了一個新的 ASP.NET 外掛程式(代號為“Atlas”),主要是為了充分利用用戶端 JavaScript、DHTML 和 XMLHttpRequest 對象。其目的是協助開發人員建立更具互動性的支援 AJAX 的 Web 應用程式。此架構從此更名為正式名稱 Microsoft AJAX Library 和 ASP.NET 2.0 AJAX Extensions,它提供了許多出色的功能,包括用戶端資料繫結、DHTML 動畫和行為以及使用 UpdatePanel 實現的完善的對用戶端 POST 回調的攔截。這些功能中的許多功能依賴的是以易於通過用戶端 JavaScript 調用進行分析和互動的形式從伺服器非同步檢索資料的能力。本月專欄的主題便是這一新的非常有用的能力,即在支援 ASP.NET 2.0 AJAX Extensions 的頁面中通過用戶端 JavaScript 調用伺服器端 Web 服務的能力。 使用 AJAX 調用 Web 服務 如果您曾經使用過 Microsoft .NET Framework 中的 Web 服務,無論是使用 wsel.exe 公用程式建立代理還是使用 Visual Studio 的“添加 Web 參考”功能,您就會習慣於使用 .NET 類型調用 Web 服務。實際上,通過 .NET 代理調用 Web 服務方法與在其他類上調用方法非常相似。代理會根據您傳遞的參數準備 XML,它會妥善地將它收到的 XML 響應轉換成代理方法指定的 .NET 類型。開發人員可以非常方便地利用 .NET Framework 使用 Web 服務端點,這也使目前面向服務的應用程式變得可行。 ASP.NET 2.0 AJAX Extensions 使得在瀏覽器中啟動並執行用戶端 JavaScript 實現了無縫的、與 Web 服務完全相同的代理產生體驗。您可以編寫一個在您的伺服器上承載的 .asmx 檔案,並通過一個用戶端 JavaScript 類調用該服務上方法。例如,圖 1 顯示了一個簡單的 .asmx 服務,該服務實現了類比的股票報價檢索(使用隨機資料)。 除了標準的 .asmx Web 服務屬性外,此服務還增添了 ScriptService 屬性,使其同樣可適用於 JavaScript 用戶端。如果此 .asmx 檔案部署在支援 ASP.NET AJAX 的 Web 應用程式中,您可以通過為您的 .aspx 檔案中的 ScriptManager 控制項添加 ServiceReference,從 JavaScript 調用服務的方法(當您使用支援 ASP.NET AJAX 的網站模板在 Visual Studio 中建立網站時,此控制項會自動添加到您的 default.aspx 頁面中): <asp:ScriptManager ID="_scriptManager" runat="server"> <Services> <asp:ServiceReference Path="StockQuoteService.asmx" /> </Services> </asp:ScriptManager> 現在您可以通過任何用戶端 JavaScript 常式,使用 MsdnMagazine.StockQuoteService 類調用服務的任何方法。由於調用的基本機制在本質上是非同步,因此沒有同步方法可用。每個代理方法帶有一個額外的參數(除了標準的輸入參數外),該參數引用了另一個在該方法完成時非同步呼叫的用戶端 JavaScript 函數。圖 2 中顯示的樣本頁面使用用戶端 JavaScript 將調用股票報價 Web 服務的結果顯示在頁面上的標籤 (span) 中。 如果用戶端 Web 服務調用出現了問題,您一定希望讓用戶端知道這一情況,通常明智的做法是在出現錯誤、中止或逾時時,調用另一個方法進行傳遞。例如,您可以按如下所示更改上面顯示的 OnLookup 方法,並額外添加一個 OnError 方法,以顯示所有問題: function OnLookup() { var stb = document.getElementById("_symbolTextBox"); MsdnMagazine.StockQuoteService.GetStockQuote( stb.value, OnLookupComplete, OnError); } function OnError(result) { alert("Error: " + result.get_message()); } 這樣,如果 Web 服務調用失敗,您將通過警示方塊通知用戶端。您還可以在從用戶端發出的對任何 Web 服務的調用中加入 userContext 參數,該參數是作為 Web 方法的最後參數傳入的任一字元串,它將作為額外的參數傳播給成功和失敗的方法。在這種情況下,將被請求股票的實際符號作為 userContext 進行傳遞會比較有意義,這樣您就可在 OnLookupComplete 方法中顯示它: function OnLookup() { var stb = document.getElementById("_symbolTextBox"); MsdnMagazine.StockQuoteService.GetStockQuote( stb.value, OnLookupComplete, OnError, stb.value); } function OnLookupComplete(result, userContext) { // userContext contains symbol passed into method var res = document.getElementById("_resultLabel"); res.innerHTML = userContext + " : <b>" + result + "</b>"; } 如果您發現您要對某個 Web 服務進行多個不同的調用,並且對於每個調用您重複使用相同的錯誤和/或完成方法,那麼您還可以設定全域預設的失敗和成功的回調方法。這就避免了在每次調用時都必須指定一對回調方法,而且您還可以為各個方法分別進行選擇,使其忽略全域定義。以下是 OnLookup 方法的樣本,其中設定了全域的(而不是對單個調用的)預設的成功和失敗的回調方法。 // Set default callbacks for stock quote service MsdnMagazine.StockQuoteService.set_defaultSucceededCallback( OnLookupComplete); MsdnMagazine.StockQuoteService.set_defaultFailedCallback( OnError); function OnLookup() { MsdnMagazine.StockQuoteService.GetStockQuote(stb.value); } 還有一種可以為您的 Web 服務方法構建完整的 .asmx 檔案的有趣方法,就是將 Web 服務方法直接內嵌在頁類中。如果為您希望調用的方法構建完整的 Web 服務端點沒有意義,那麼您可以在您的頁面中公開一個可通過用戶端 JavaScript 調用的 Web 方法,做法是向頁面中添加一個伺服器端方法(直接在頁面中添加或者以程式碼後置的方式添加)並用 WebMethod 屬性對其進行批註。然後您就可以通過用戶端對象 PageMethods 調用它了。圖 3 中的樣本顯示的是經過重新編寫的股票報價服務樣本,它完全包含在一個頁中,而不是分割到各個 Web 服務中。 請記住,這些用戶端代理只能由 ASP.NET .asmx 端點、Windows Communication Foundation .svc 端點或直接內嵌在頁面中的 WebMethod 產生,而且不是調用任意 Web 服務的通用機制。實際上,對於基本的 XmlHTTPRequest 對象有一般性限制,請求的範圍僅限於載入頁面的域(出於安全的原因),因此這一做法無法用於調用任意 Web 服務,無論用戶端代理是否支援此操作。如果您發現需要調用外部 Web 服務,最好在您調用外部 Web 服務的 .NET 代理類(使用 wsdl.exe 或 Visual Studio 中的“添加 Web 參考”產生)的應用程式中設定一個橋接的 .asmx 端點。 工作原理 您可以採用標準的 .asmx Web 服務,幾乎不做任何更改即可在瀏覽器中通過用戶端 JavaScript 對其進行訪問,乍看起來有些匪夷所思。秘密就在於註冊了一個新的 .asmx HTTP 處理常式,並將其添加到了每個支援 ASP.NET AJAX 的網站的設定檔中: <httpHandlers> <remove verb="*" path="*.asmx"/> <add verb="*" path="*.asmx" type="Microsoft.Web.Services.ScriptHandlerFactory" validate="false"/> </httpHandlers> 如果對一個 .asmx 端點進行標準的 Web 服務請求,則這個新註冊的處理常式將調用標準 Web 服務處理常式 (System.Web.Services.Protocols.WebServiceHandlerFactory)。但是,如果請求在 URL 中有尾碼 /js 或者包含帶有 mn= 變數的查詢字串(如 ?mn=GetStockQuote),則處理常式會返回一個 JavaScript 塊,為 Web 服務建立一個用戶端代理(帶有 /js 的情況),或者會調用 WebService 衍生類別中定義的相應方法,並把響應打包在 JavaScript Object Notation (JSON) 編碼的字串中(帶有 ?mn= 的情況)。 當頁麵包含對 .asmx 服務的用戶端引用(通過 ScriptManager 控制項中的 ServiceReference 元素)時,它會注入使用尾碼 /js 引用 .asmx 檔案的指令碼元素,從而在用戶端建立代理。例如,我在上面構建的股票報價頁面會在其中顯示以下指令碼元素: <script src="StockQuoteService.asmx/js" type="text/javascript"></script> 當然,這是在添加了對 Microsoft AJAX Library 的指令碼引用的基礎上,AJAX Library 中包含了與此代理進行互動所需的用戶端功能。如果您嘗試自己導航至此端點,您將看到以下 JavaScript(部分): Type.registerNamespace('MsdnMagazine'); MsdnMagazine.StockQuoteService=function() { this._timeout = 0; this._userContext = null; this._succeeded = null; this._failed = null; } MsdnMagazine.StockQuoteService.prototype={ GetStockQuote:Sys.Net._WebMethod._createProxyMethod(this, "GetStockQuote", "MsdnMagazine.StockQuoteService.GetStockQuote", "symbol"), ... } 此 JavaScript 使用每個包含 ScriptManager 控制項的頁面中所包含的 Microsoft AJAX Library 的功能(如命名空間和 WebMethod 類)。此 JavaScript 建立的代理方法經過初始化,利用此例中的查詢字串 ?mn=GetStockQuote 調用 .asmx 端點,因此無論您何時從用戶端調用 MsdnMagazine.StockQuoteService.GetStockQuote,它都會變成對同一 .asmx 端點的非同步 Web 請求。將用戶端代理產生和伺服器端對 JavaScript 發出的 Web 服務調用的支援相結合,意味著您可以以一種直觀的方式包含對您的 .asmx Web 服務的用戶端調用。 序列化 基於 AJAX 的 Web 服務的預設序列化是 JSON。如果您注意到上一部分所顯示的一系列頁面操作,會發現 Web 服務請求和響應的主體部分類似於: Request: {"symbol":"ABC"} Response: 51 這當然不是您在調用 .asmx Web 服務時所習慣看到的標準 XML 格式,因為 .asmx 端點在構建時已序列化到 XML 中,所以 ASP.NET 2.0 AJAX Extensions 所增加一個主要內容便是 JSON 序列化程式。實際上共有兩個序列化程式:一個在 JavaScript 中實現,用於用戶端,一個在 .NET 中實現,用於伺服器,尤其用在 AJAX 用戶端調用 .asmx 端點時。伺服器端序列化程式可通過 Microsoft.Web.Script.Serialization.JavaScriptSerializer 使用,用戶端序列化程式可通過 Sys.Serialization.JavaScriptSerializer 使用。使用 JSON 作為基於 XML 的序列化格式的一個主要優勢是您只需簡單地求得 JSON 字串的值即可對 JavaScript 中的對象還原序列化。用戶端序列化程式類的還原序列化方法最終會變得非常簡短(去掉了錯誤檢查): Sys.Serialization.JavaScriptSerializer.deserialize= function(){eval('('+data+')');} 而另一方面,JavaScriptSerializer 的序列化方法卻更複雜了。使用 JSON 的另一優勢就是它與對應的 XML 相比,其表示形式更加精簡。 與標準 Web 服務使用 XmlSerializer 將類型序列化到 XML 中非常類似,您可以採用幾乎任何 .NET 類型並使用 JavaScriptSerializer 將其序列化到 JSON 中。如果您希望親自嘗試,只需調用 JavaScriptSerializer 類的 Serialize 方法。圖 4 顯示了一個樣本控制台應用程式,此例中,它對複雜的 Person 類型( 5 所示)進行序列化。(該程式必須包含對 Microsoft.Web.Extensions.dll 的程式集引用,該 DLL 隨 ASP.NET AJAX Extensions 安裝在全域組件快取 (GAC) 中。) 控制台應用程式的輸出將是 JSON 格式的 Person 類,或: {"Married":true,"Age":33,"FirstName":"Bob","LastName":"Smith"} 正像 XmlSerializer 那樣,JavaScriptSerializer 將僅對一種類型的公用可訪問資料進行序列化,並且不支援對循環參考的解析。但任何可由標準 .asmx Web 服務序列化的類型也都能與此序列化程式一起正常工作(當然其中包括 DataSet)。鑒於這一點,您可以構建更複雜的 Web 服務,因為它處理複雜類型就像 .asmx 檔案中定義的任何基於 SOAP 的 Web 服務一樣輕鬆。 圖 6 中的樣本顯示了一個名為 MarriageService 的 Web 服務,它實現了 Marry 方法,它採用兩個 Person 對象(正如前面定義的)並對其屬性進行相應的修改。(本期的代碼下載部分包含有此例附帶的 ASP.NET 頁面。) 如果您選擇在您的用戶端指令碼中使用 XML,該選項仍然可用。除了在定義 Web 服務時使用的標準 WebMethod 屬性外,Microsoft.Web.Script.Services 命名空間中還有一個名為 ScriptMethod 的新屬性,它具有 ResponseFormat 特性,該特性可設定為 Json 或 Xml(預設值為 Json)。 namespace PS { [ScriptService] [WebService(Namespace = "http://pluralsight.com/ws")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class StockQuoteService : WebService { [WebMethod] public int GetStockQuote(string symbol) { return (new Random()).Next(0, 120); } } } 這樣,序列化的響應將為: <?xml version="1.0" encoding="utf-8"?><int>74</int> 當您通過用戶端 JavaScript 調用此方法來處理 XML 響應時,怎樣選擇由您自己決定。如果您計劃對其執行轉換,或者已經在使用 MSXML,那麼這會非常有用。 總結 有必要指出的是,ASP.NET 2.0 AJAX Extensions 提供了兩個預先構建的服務,用於通過用戶端代碼訪問特定 ASP.NET 2.0 應用程式服務,它們是:ProfileService 和 AuthenicationService。使用這兩個用戶端代理類,您可以為單個用戶端設定和檢索設定檔值,並完全在用戶端指令碼中執行身分識別驗證(通過預設的成員資格提供者)和授予身分識別驗證 cookies。 儘管許多有關 ASP.NET 2.0 AJAX Extensions 的討論和示範都偏重於介紹那些使使用者介面具備更高響應能力的漂亮控制項,但是能夠直接通過用戶端 JavaScript 調用 Web 服務是最吸引人、最實用的功能之一。憑藉完善的 .NET Framework/JSON 序列化程式、與熟悉的 .asmx Web 服務的直接整合、對批處理的支援和自動產生的與外部 Web 服務的橋接,如此全面且深入地支援 Web 服務可能會使這一功能成為所有功能中最迷人的功能。 將您想向 Fritz 詢問的問題和提出的意見發送至 xtrmasp@microsoft.com..
NEW: Explore the sample code online! - or - 代碼下載位置: ExtremeASPNET2007_01.exe (160KB) |
Fritz Onion是 Microsoft .NET 培訓供應商 Pluralsight 的創始人之一,負責 Web 開發課程。Fritz 是《Essential ASP.NET》(Addison Wesley,2003)和《Essential ASP.NET 2.0》(Addison Wesley,2006)的作者。您可以通過 pluralsight.com/fritz 與他聯絡。 摘自 January 2007 期刊 MSDN Magazine. |