共用方式為


了解 ASP.NET AJAX Web 服務

作者:Scott Cate

Web 服務是 .NET 架構不可或缺的一部分,可提供跨平臺解決方案,在分散式系統之間交換資料。 雖然 Web 服務通常用來允許不同的作業系統、物件模型和程式設計語言來傳送和接收資料,但它們也可以用來動態將資料插入 AJAX 頁面 ASP.NET,或將資料從頁面傳送至後端系統。 這一切都可以完成,而不需要依賴回傳作業。

使用 ASP.NET AJAX 呼叫 Web 服務

Dan Wahlin

Web 服務是 .NET 架構不可或缺的一部分,可提供跨平臺解決方案,在分散式系統之間交換資料。 雖然 Web 服務通常用來允許不同的作業系統、物件模型和程式設計語言來傳送和接收資料,但它們也可以用來動態將資料插入 AJAX 頁面 ASP.NET,或將資料從頁面傳送至後端系統。 這一切都可以完成,而不需要依賴回傳作業。

雖然 ASP.NET AJAX UpdatePanel 控制項提供簡單的方式讓 AJAX 啟用任何 ASP.NET 網頁,但有時候您可能需要動態存取伺服器上的資料而不使用 UpdatePanel。 在本文中,您將瞭解如何在 AJAX 頁面 ASP.NET 內建立和使用 Web 服務來完成此作業。

本文將著重于核心 ASP.NET AJAX 延伸模組中可用的功能,以及名為 AutoCompleteExtender 之 ASP.NET AJAX 工具組中已啟用 Web 服務的控制項。 涵蓋的主題包括定義已啟用 AJAX 的 Web 服務、建立用戶端 Proxy 並使用 JavaScript 呼叫 Web 服務。 您也將瞭解如何直接對網頁方法進行 Web 服務呼叫 ASP.NET。

Web 服務組態

使用 Visual Studio 2008 建立新的網站專案時,web.config檔案有一些可能不熟悉舊版 Visual Studio 使用者的新新增專案。 其中一些修改會將 「asp」 前置詞對應至 ASP.NET AJAX 控制項,以便用於頁面,而其他修改則會定義必要的 HttpHandlers 和 HttpModules。 清單 1 顯示對影響 Web 服務呼叫之 web.config 中元素所做的 <httpHandlers> 修改。 用來處理 .asmx 呼叫的預設 HttpHandler 會移除,並以位於 System.Web.Extensions.dll 元件中的 ScriptHandlerFactory 類別取代。 System.Web.Extensions.dll包含 ASP.NET AJAX 所使用的所有核心功能。

清單 1. ASP.NET AJAX Web 服務處理常式組態

<httpHandlers>
     <remove verb="*" path="*.asmx"/>
     <add verb="*" path="*.asmx" validate="false"
          type="System.Web.Script.Services.ScriptHandlerFactory,
          System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
          PublicKeyToken=31bf3856ad364e35"/>
</httpHandlers>

這個 HttpHandler 取代是為了讓 JavaScript 物件標記法 (JSON) 呼叫,以使用 JavaScript Web Service Proxy 從 AJAX 頁面 ASP.NET .NET Web 服務進行呼叫。 ASP.NET AJAX 會將 JSON 訊息傳送至 Web 服務,而不是標準簡單物件存取通訊協定, (SOAP) 呼叫通常與 Web 服務相關聯。 這會導致整體要求和回應訊息較小。 它也允許更有效率的用戶端處理資料,因為 ASP.NET AJAX JavaScript 程式庫已優化以使用 JSON 物件。 清單 2 和清單 3 顯示 Web 服務要求和序列化為 JSON 格式的回應訊息範例。 清單 2 中顯示的要求訊息會傳遞具有值「比利時」的國家/地區參數,而清單 3 中的回應訊息則會傳遞 Customer 物件的陣列及其相關聯的屬性。

清單 2. Web 服務要求訊息序列化為 JSON

{"country":"Belgium"}

> [!注意] 作業名稱定義為 Web 服務的 URL 的一部分;此外,要求訊息不一定會透過 JSON 提交。 Web 服務可以使用 ScriptMethod 屬性,並將 UseHttpGet 參數設定為 true,這會導致參數透過查詢字串參數傳遞。

清單 3. 序列化為 JSON 的 Web 服務回應訊息

[{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Maison
     Dewey","CustomerID":"MAISD","ContactName":"Catherine
     Dewey"},{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Suprêmes
     délices","CustomerID":"SUPRD","ContactName":"Pascale
     Cartrain"}]

在下一節中,您將瞭解如何建立 Web 服務,以處理 JSON 要求訊息,以及使用簡單和複雜類型回應。

建立 AJAX-Enabled Web 服務

ASP.NET AJAX 架構提供數種不同的方式來呼叫 Web 服務。 您可以使用 ASP.NET AJAX Toolkit) 或 JavaScript 中提供的 AutoCompleteExtender 控制項 (。 不過,在呼叫服務之前,您必須啟用 AJAX,才能由用戶端腳本程式碼加以呼叫。

不論您不熟悉 ASP.NET Web 服務,您都會發現建立和啟用 AJAX 的服務很簡單。 .NET Framework 支援在 2002 年初始版本之後建立 ASP.NET Web 服務,而 ASP.NET AJAX 延伸模組提供以 .NET Framework 預設功能集為建置的其他 AJAX 功能。 Visual Studio .NET 2008 Beta 2 內建支援建立 .asmx Web 服務檔案,並會自動從 System.Web.Services.WebService 類別衍生相關聯的程式碼。 當您將方法新增至 類別時,您必須套用 WebMethod 屬性,才能讓 Web 服務取用者呼叫它們。

清單 4 顯示將 WebMethod 屬性套用至名為 GetCustomersByCountry () 的方法範例。

清單 4. 在 Web 服務中使用 WebMethod 屬性

[WebMethod]
public Customer[] GetCustomersByCountry(string country)
{
     return Biz.BAL.GetCustomersByCountry(country);
}

GetCustomersByCountry () 方法接受國家/地區參數,並傳回 Customer 物件陣列。 傳遞至 方法的國家/地區值會轉送至商務層類別,接著呼叫資料層類別以從資料庫擷取資料、填入 Customer 物件屬性並傳回陣列。

使用 ScriptService 屬性

新增 WebMethod 屬性可讓將標準 SOAP 訊息傳送至 Web 服務的用戶端呼叫 GetCustomersByCountry () 方法,但不允許從現成的 AJAX 應用程式 ASP.NET 進行 JSON 呼叫。 若要允許進行 JSON 呼叫,您必須將 AJAX 擴充 ScriptService 功能的 ASP.NET 屬性套用至 Web 服務類別。 這可讓 Web 服務傳送使用 JSON 格式化的回應訊息,並允許用戶端腳本藉由傳送 JSON 訊息來呼叫服務。

清單 5 顯示將 ScriptService 屬性套用至名為 CustomersService 的 Web 服務類別的範例。

清單 5. 使用 ScriptService 屬性啟用 AJAX 的 Web 服務

[System.Web.Script.Services.ScriptService]
[WebService(Namespace = "http://xmlforasp.net")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CustomersService : System.Web.Services.WebService
{
     [WebMethod]
     public Customer[] GetCustomersByCountry(string country)
     {
          return Biz.BAL.GetCustomersByCountry(country);
     }
}

ScriptService 屬性可作為標記,指出可以從 AJAX 腳本程式碼呼叫它。 它實際上不會處理在幕後發生的任何 JSON 序列化或還原序列化工作。 ScriptHandlerFactory (在 web.config) 和其他相關類別中設定,會執行大量 JSON 處理。

使用 ScriptMethod 屬性

ScriptService 屬性是唯一 ASP.NET AJAX 屬性,必須定義在 .NET Web 服務中,才能 ASP.NET AJAX 頁面使用。 不過,另一個名為 ScriptMethod 的屬性也可以直接套用至服務中的 Web 方法。 ScriptMethod 定義三個屬性,包括 UseHttpGetXmlSerializeStringResponseFormat 。 如果 Web 方法接受的要求類型必須變更為 GET,當 Web 方法需要以 或 XmlElement 物件的形式 XmlDocument 傳回原始 XML 資料,或從服務傳回的資料一律應該序列化為 XML 而非 JSON 時,變更這些屬性的值可能會很有用。

當 Web 方法應該接受 GET 要求,而不是 POST 要求時,可以使用 UseHttpGet 屬性。 要求會使用 URL 傳送,並將 Web 方法輸入參數轉換成 QueryString 參數。 UseHttpGet 屬性預設為 false,而且只有在已知作業安全且敏感性資料未傳遞至 Web 服務時,才會設定 true 為 。 清單 6 顯示搭配 UseHttpGet 屬性使用 ScriptMethod 屬性的範例。

清單 6. 搭配 UseHttpGet 屬性使用 ScriptMethod 屬性。

[WebMethod]
[ScriptMethod(UseHttpGet = true)]
public string HttpGetEcho(string input)
{
     return input;
}

呼叫清單 6 中顯示的 HttpGetEcho Web 方法時所傳送的標頭範例如下:

GET /CustomerViewer/DemoService.asmx/HttpGetEcho?input=%22Input Value%22 HTTP/1.1

除了允許 Web 方法接受 HTTP GET 要求之外,當需要從服務傳回 XML 回應而非 JSON 時,也可以使用 ScriptMethod 屬性。 例如,Web 服務可能會從遠端網站擷取 RSS 摘要,並將其傳回為 XmlDocument 或 XmlElement 物件。 XML 資料的處理接著可以在用戶端上發生。

清單 7 顯示使用 ResponseFormat 屬性指定應該從 Web 方法傳回 XML 資料的範例。

清單 7. 搭配 ResponseFormat 屬性使用 ScriptMethod 屬性。

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlElement GetRssFeed(string url)
{
     XmlDocument doc = new XmlDocument();
     doc.Load(url);
     return doc.DocumentElement;
}

ResponseFormat 屬性也可以與 XmlSerializeString 屬性搭配使用。 XmlSerializeString 屬性的預設值為 false,這表示當屬性設定 ResponseFormat.Xml 為 時 ResponseFormat ,除了從 Web 方法傳回的字串以外的所有傳回型別都會序列化為 XML。 當 設定為 trueXmlSerializeString ,從 Web 方法傳回的所有型別都會序列化為 XML,包括字串類型。 如果 ResponseFormat 屬性的值是 ResponseFormat.Json 忽略 XmlSerializeString 屬性。

清單 8 顯示使用 XmlSerializeString 屬性強制字串序列化為 XML 的範例。

清單 8. 搭配 XmlSerializeString 屬性使用 ScriptMethod 屬性

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml,XmlSerializeString=true)]
public string GetXmlString(string input)
{
     return input;
}

接下來會顯示從呼叫清單 8 所示的 GetXmlString Web 方法所傳回的值:

<?xml version="1.0"?>
     <string>Test</string>

雖然預設 JSON 格式會以跨瀏覽器方式將要求和回應訊息的整體大小降到最低,而且更容易由 ASP.NET AJAX 用戶端取用,但當 Internet Explorer 5 或更新版本的用戶端應用程式預期會從 Web 方法傳回 XML 資料時,可以使用 ResponseFormat 和 XmlSerializeString 屬性。

使用複雜類型

清單 5 顯示從 Web 服務傳回名為 Customer 的複雜類型範例。 Customer 類別會在內部定義數個不同的簡單類型,例如 FirstName 和 LastName。 在啟用 AJAX 的 Web 方法上作為輸入參數或傳回型別的複雜型別,會在傳送至用戶端之前,自動序列化為 JSON。 不過,巢狀複雜類型 (在內部定義于另一個類型) 的巢狀複雜類型預設不會提供給用戶端作為獨立物件。

如果 Web 服務所使用的巢狀複雜類型也必須用於用戶端頁面中,可以將 AJAX GenerateScriptType 屬性 ASP.NET 新增至 Web 服務。 例如,清單 9 中顯示的 CustomerDetails 類別包含代表巢狀複雜類型的Address 和 Gender 屬性。

清單 9. 這裡顯示的 CustomerDetails 類別包含兩個巢狀複雜類型。

public class CustomerDetails : Customer
{
     public CustomerDetails()
     {
     }
     Address _Address;
     Gender _Gender = Gender.Unknown;
     public Address Address
     {
          get { return _Address; }
          set { _Address = value; }
     }
     public Gender Gender
     {
          get { return _Gender; }
          set { _Gender = value; }
     }
}

在清單 9 所示的 CustomerDetails 類別內定義的 Address 和 Gender 物件不會自動透過 JavaScript 在用戶端上使用,因為它們是巢狀類型, (Address 是類別,而 Gender 是列舉) 。 在 Web 服務中使用的巢狀類型必須在用戶端上使用時,可以使用稍早所述的 GenerateScriptType 屬性, (請參閱清單 10) 。 在從服務傳回不同的巢狀複雜類型時,可以多次新增這個屬性。 它可以直接套用至 Web 服務類別或高於特定 Web 方法。

清單 10。 使用 GenerateScriptService 屬性來定義應該可供用戶端使用的巢狀類型。

[System.Web.Script.Services.ScriptService]
[System.Web.Script.Services.GenerateScriptType(typeof(Address))]
[System.Web.Script.Services.GenerateScriptType(typeof(Gender))]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class NestedComplexTypeService : System.Web.Services.WebService
{
     //Web Methods
}

藉由將 GenerateScriptType 屬性套用至 Web 服務,位址和性別類型會自動供用戶端 ASP.NET AJAX JavaScript 程式碼使用。 在 Web 服務上新增 GenerateScriptType 屬性,自動產生並傳送至用戶端的 JavaScript 範例會顯示在清單 11 中。 您將在本文稍後瞭解如何使用巢狀複雜類型。

清單 11. 提供給 ASP.NET AJAX 頁面的巢狀複雜類型。

if (typeof(Model.Address) === 'undefined')
{
     Model.Address=gtc("Model.Address");
     Model.Address.registerClass('Model.Address');
}
Model.Gender = function() { throw Error.invalidOperation(); }
Model.Gender.prototype = {Unknown: 0,Male: 1,Female: 2}
Model.Gender.registerEnum('Model.Gender', true);

既然您已瞭解如何建立 Web 服務,並讓它們可供 ASP.NET AJAX 頁面存取,讓我們看看如何建立和使用 JavaScript Proxy,以便將資料擷取或傳送至 Web 服務。

建立 JavaScript Proxy

呼叫標準 Web 服務 (.NET 或其他平臺) 通常牽涉到建立 Proxy 物件,以防止傳送 SOAP 要求和回應訊息的複雜性。 透過 ASP.NET AJAX Web 服務呼叫,您可以建立 JavaScript Proxy 並用來輕鬆呼叫服務,而不必擔心序列化和還原序列化 JSON 訊息。 JavaScript Proxy 可以使用 ASP.NET AJAX ScriptManager 控制項自動產生。

使用 ScriptManager 的 Services 屬性,建立可呼叫 Web 服務的 JavaScript Proxy。 此屬性可讓您定義 ASP.NET AJAX 頁面可以非同步呼叫的一或多個服務,以傳送或接收資料,而不需要回傳作業。 您可以使用 ASP.NET AJAX ServiceReference 控制項,並將 Web 服務 URL 指派給控制項 Path 的 屬性來定義服務。 清單 12 顯示參考名為 CustomersService.asmx 的服務範例。

<asp:ScriptManager ID="ScriptManager1" runat="server">
     <Services>
          <asp:ServiceReference Path="~/CustomersService.asmx" />
     </Services>
</asp:ScriptManager>

清單 12. 定義 ASP.NET AJAX 頁面中使用的 Web 服務。

透過 ScriptManager 控制項新增 CustomersService.asmx 的參考,會導致頁面動態產生及參考 JavaScript Proxy。 Proxy 是內嵌的,方法是使用 < 腳本 > 標籤,並藉由呼叫 CustomersService.asmx 檔案並附加 /js 至其結尾來動態載入。 下列範例示範在偵錯停用 web.config 時,JavaScript Proxy 如何內嵌在頁面中:

<script src="CustomersService.asmx/js" type="text/javascript"></script>

> [!注意] 如果您想要查看產生的實際 JavaScript Proxy 程式碼,您可以在 Internet Explorer 的位址方塊中輸入所需 .NET Web 服務的 URL,並將 /js 附加至其結尾。

如果在 web.config啟用偵錯,JavaScript Proxy 的偵錯版本將會內嵌在頁面中,如下所示:

<script src="CustomersService.asmx/jsdebug" type="text/javascript"></script>

ScriptManager 所建立的 JavaScript Proxy 也可以直接內嵌到頁面,而不是使用 < 腳本 > 標記的 src 屬性來參考。 您可以將 ServiceReference 控制項的 InlineScript 屬性設定為 true, (預設值為 false) 來完成。 這在 Proxy 未跨多個頁面共用,以及您想要減少對伺服器進行的網路呼叫數目時,這非常有用。 當 InlineScript 設定為 true 時,瀏覽器將不會快取 Proxy 腳本,因此在 ASP.NET AJAX 應用程式中多個頁面使用 Proxy 時,建議使用 false 的預設值。 接下來會顯示使用 InlineScript 屬性的範例:

<asp:ServiceReference InlineScript="true" Path="~/CustomersService.asmx"/>

使用 JavaScript Proxy

使用 ScriptManager 控制項 ASP.NET AJAX 頁面參考 Web 服務之後,就可以呼叫 Web 服務,並使用回呼函式來處理傳回的資料。 如果一個命名空間存在) 、類別名稱和 Web 方法名稱,則會呼叫 Web 服務 (。 傳遞至 Web 服務的任何參數都可以與處理傳回資料的回呼函式一起定義。

清單 13 會顯示使用 JavaScript Proxy 呼叫名為 GetCustomersByCountry () 的 Web 方法範例。 當使用者按一下頁面上的按鈕時,會呼叫 GetCustomersByCountry () 函式。

清單 13. 使用 JavaScript Proxy 呼叫 Web 服務。

function GetCustomerByCountry()
{
     var country = $get("txtCountry").value;
     InterfaceTraining.CustomersService.GetCustomersByCountry(country, OnWSRequestComplete);
}
function OnWSRequestComplete(results)
{
     if (results != null)
     {
          CreateCustomersTable(results);
          GetMap(results);
     }
}

此呼叫會參考服務中定義的 InterfaceTraining 命名空間、CustomersService 類別和 GetCustomersByCountry Web 方法。 它會傳遞從文字方塊中取得的國家/地區值,以及名為 OnWSRequestComplete 的回呼函式,在非同步 Web 服務呼叫傳回時應該叫用。 OnWSRequestComplete 會處理從服務傳回的客戶物件陣列,並將其轉換成頁面中顯示的資料表。 從呼叫產生的輸出會顯示在圖 1 中。

透過對 Web 服務進行非同步 AJAX 呼叫來取得的系結資料。

圖 1:對 Web 服務進行非同步 AJAX 呼叫取得的系結資料。 (按一下即可檢視完整大小的映射)

如果應該呼叫 Web 方法,但 Proxy 不應該等候回應,JavaScript Proxy 也可以對 Web 服務進行單向呼叫。 例如,您可能想要呼叫 Web 服務來啟動工作流程之類的程式,但不要等候服務的傳回值。 在需要對服務進行單向呼叫的情況下,只要省略清單 13 中顯示的回呼函式。 由於未定義回呼函式,Proxy 物件不會等待 Web 服務傳回資料。

處理錯誤

Web 服務的非同步回呼可能會遇到不同類型的錯誤,例如網路關閉、Web 服務無法使用或傳回例外狀況。 幸運的是,ScriptManager 所產生的 JavaScript Proxy 物件允許定義多個回呼來處理錯誤和失敗,以及稍早顯示的成功回呼。 您可以在呼叫 Web 方法的標準回呼函式之後立即定義錯誤回呼函式,如清單 14 所示。

清單 14. 定義錯誤回呼函式並顯示錯誤。

function GetCustomersByCountry() 
{
     var country = $get("txtCountry").value;
     InterfaceTraining.CustomersService.GetCustomersByCountry(country, 
          OnWSRequestComplete, OnWSRequestFailed);
}
function OnWSRequestFailed(error)
{
     alert("Stack Trace: " + error.get_stackTrace() + "/r/n" +
          "Error: " + error.get_message() + "/r/n" +
          "Status Code: " + error.get_statusCode() + "/r/n" +
          "Exception Type: " + error.get_exceptionType() + "/r/n" +
          "Timed Out: " + error.get_timedOut());
}

呼叫 Web 服務時所發生的任何錯誤都會觸發 OnWSRequestFailed () 回呼函式,以接受以參數表示錯誤的物件。 錯誤物件會公開數個不同的函式,以判斷錯誤的原因,以及呼叫是否逾時。清單 14 顯示使用不同錯誤函式的範例,圖 2 顯示函式所產生的輸出範例。

呼叫 AJAX 錯誤函式 ASP.NET 所產生的輸出。

圖 2:呼叫 ASP.NET AJAX 錯誤函式所產生的輸出。 (按一下即可檢視完整大小的映射)

處理從 Web 服務傳回的 XML 資料

稍早您已瞭解 Web 方法如何使用 ScriptMethod 屬性及其 ResponseFormat 屬性傳回原始 XML 資料。 當 ResponseFormat 設定為 ResponseFormat.Xml時,從 Web 服務傳回的資料會序列化為 XML,而不是 JSON。 當 XML 資料需要直接傳遞至用戶端以使用 JavaScript 或 XSLT 進行處理時,這非常有用。 目前,Internet Explorer 5 或更新版本會提供最佳的用戶端物件模型來剖析和篩選 XML 資料,因為 MSXML 的內建支援。

從 Web 服務擷取 XML 資料與擷取其他資料類型不同。 首先,叫用 JavaScript Proxy 來呼叫適當的函式並定義回呼函式。 呼叫傳回之後,您就可以在回呼函式中處理資料。

清單 15 示範如何呼叫名為 GetRssFeed () 的 Web 方法,以傳回 XmlElement 物件。 GetRssFeed () 接受代表要擷取之 RSS 摘要 URL 的單一參數。

清單 15. 使用從 Web 服務傳回的 XML 資料。

function GetRss()
{
     InterfaceTraining.DemoService.GetRssFeed(
          "https://blogs.interfacett.com/dan-wahlins-blog/rss.xml",
          OnWSRequestComplete);
}
function OnWSRequestComplete(result)
{
     if (document.all) //Filter for IE DOM since other browsers are limited
     {
          var items = result.selectNodes("//item");
          for (var i=0;i<items.length;i++)
          {
               var title = items[i].selectSingleNode("title").text;
               var href = items[i].selectSingleNode("link").text;
               $get("divOutput").innerHTML +=
               "<a href='" + href + "'>" + title + "</a><br/>";
          }
     }
     else
     {
          $get("divOutput").innerHTML = "RSS only available in IE5+";
     }
}

本範例會將 URL 傳遞至 RSS 摘要,並在 OnWSRequestComplete () 函式中處理傳回的 XML 資料。 OnWSRequestComplete () 會先檢查瀏覽器是否為 Internet Explorer,以瞭解 MSXML 剖析器是否可用。 如果是,則會使用 XPath 語句來尋找 RSS 摘要內的所有 < 專案 > 標記。 接著會逐一查看每個專案,並找到並處理相關聯的 < 標題 > 和 < 連結 > 標籤,以顯示每個專案的資料。 圖 3 顯示透過 JavaScript Proxy 對 GetRssFeed () Web 方法進行 ASP.NET AJAX 呼叫所產生的輸出範例。

處理複雜類型

Web 服務接受或傳回的複雜類型會自動透過 JavaScript Proxy 公開。 不過,除非 GenerateScriptType 屬性如先前所述套用至服務,否則用戶端無法直接存取巢狀複雜類型。 為什麼您想要在用戶端上使用巢狀複雜類型?

若要回答此問題,假設 AJAX 頁面 ASP.NET 顯示客戶資料,並允許終端使用者更新客戶的位址。 如果 Web 服務指定 Address 類型 (CustomerDetails 類別內定義的複雜類型,) 可以傳送至用戶端,則更新程式可以分成不同的函式,以便重複使用程式碼。

從呼叫傳回 RSS 資料的 Web 服務建立輸出。

圖 3:從呼叫傳回 RSS 資料的 Web 服務建立輸出。 (按一下即可檢視完整大小的映射)

清單 16 顯示用戶端程式代碼的範例,該程式碼會叫用 Model 命名空間中定義的 Address 物件、填入更新的資料,並將它指派給 CustomerDetails 物件的 Address 屬性。 CustomerDetails 物件接著會傳遞至 Web 服務進行處理。

清單 16. 使用巢狀複雜類型

function UpdateAddress()
{
     var cust = new Model.CustomerDetails();
     cust.CustomerID = $get("hidCustomerID").value;
     cust.Address = CreateAddress();
     InterfaceTraining.DemoService.UpdateAddress(cust,OnWSUpdateComplete);
}
function CreateAddress()
{
     var addr = new Model.Address();
     addr.Street = $get("txtStreet").value;
     addr.City = $get("txtCity").value;
     addr.State = $get("txtState").value;
     return addr;
}
function OnWSUpdateComplete(result)
{
     alert("Update " + ((result)?"succeeded":"failed")+ "!");
}

建立和使用 Page 方法

Web 服務提供絕佳的方式,讓重複使用的服務公開給各種用戶端,包括 ASP.NET AJAX 頁面。 不過,在某些情況下,頁面需要擷取其他頁面不會使用或共用的資料。 在此情況下,讓 .asmx 檔案允許頁面存取資料可能似乎過度,因為服務只供單一頁面使用。

ASP.NET AJAX 提供另一種機制,讓您不需要建立獨立 .asmx 檔案,即可進行類似 Web 服務的呼叫。 這是使用稱為「頁面方法」的技術來完成。 頁面方法是靜態 (直接內嵌在網頁或程式碼旁檔案 VB.NET) 中,且其中已套用 WebMethod 屬性的方法共用。 藉由套用 WebMethod 屬性,即可使用名為 PageMethods 的特殊 JavaScript 物件,在執行時間動態建立。 PageMethods 物件可作為 Proxy,可防護您免于 JSON 序列化/還原序列化程式。 請注意,若要使用 PageMethods 物件,您必須將 ScriptManager 的 EnablePageMethods 屬性設定為 true。

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
</asp:ScriptManager>

清單 17 示範如何在 ASP.NET 程式碼旁類別中定義兩個頁面方法的範例。 這些方法會從位於網站App_Code資料夾中的商務層類別擷取資料。

清單 17. 定義頁面方法。

[WebMethod]
public static Customer[] GetCustomersByCountry(string country)
{
     return Biz.BAL.GetCustomersByCountry(country);
}
[WebMethod]
public static Customer[] GetCustomersByID(string id)
{
     return Biz.BAL.GetCustomersByID(id);
}

當 ScriptManager 偵測到頁面上是否有 Web 方法時,它會產生先前所述的 PageMethods 物件的動態參考。 呼叫 Web 方法可藉由參考 PageMethods 類別,後面接著方法的名稱,以及應該傳遞的任何必要參數資料來完成。 清單 18 顯示呼叫稍早所示兩個頁面方法的範例。

清單 18. 使用 PageMethods JavaScript 物件呼叫頁面方法。

function GetCustomerByCountry() 
{
     var country = $get("txtCountry").value;
     PageMethods.GetCustomersByCountry(country, OnWSRequestComplete);
}
function GetCustomerByID() 
{
     var custID = $get("txtCustomerID").value;
     PageMethods.GetCustomersByID(custID, OnWSRequestComplete);
}
function OnWSRequestComplete(results) 
{
     var searchResults = $get("searchResults");
     searchResults.control.set_data(results);
     if (results != null) GetMap(results[0].Country,results);
}

使用 PageMethods 物件與使用 JavaScript Proxy 物件非常類似。 您必須先指定應該傳遞至頁面方法的所有參數資料,然後定義在非同步呼叫傳回時應該呼叫的回呼函式。 您也可以指定失敗回呼 (請參閱清單 14,以取得處理失敗) 的範例。

AutoCompleteExtender 和 ASP.NET AJAX Toolkit

ASP.NET AJAX Toolkit (可從 https://www.devexpress.com/Products/AJAX-Control-Toolkit) 提供數個可用來存取 Web 服務的控制項。 具體而言,工具組包含名為 AutoCompleteExtender 的實用控制項,可用來呼叫 Web 服務,並在頁面中顯示資料,而不需要撰寫任何 JavaScript 程式碼。

AutoCompleteExtender 控制項可用來擴充文字方塊的現有功能,並協助使用者更輕鬆地找到他們正在尋找的資料。 當控制項輸入文字方塊時,控制項可用來查詢 Web 服務,並以動態方式在文字方塊下方顯示結果。 圖 4 顯示使用 AutoCompleteExtender 控制項來顯示支援應用程式客戶識別碼的範例。 當使用者在文字方塊中輸入不同的字元時,會根據其輸入顯示不同的專案。 然後,使用者可以選取所需的客戶識別碼。

在 ASP.NET AJAX 頁面中使用 AutoCompleteExtender,需要將AjaxControlToolkit.dll元件新增至網站的 bin 資料夾。 新增工具組元件之後,您會想要在 web.config 中參考它,讓它所包含的控制項可供應用程式中的所有頁面使用。 這可藉由在web.config的 < 控制項 > 標籤內新增下列標記來完成:

<add namespace="AjaxControlToolkit" assembly="AjaxControlToolkit" tagPrefix="ajaxToolkit"/>

如果您只需要在特定頁面中使用 控制項,您可以將 Reference 指示詞新增至頁面頂端,而不是更新web.config來加以參考:

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" 
     TagPrefix="ajaxToolkit" %>

使用 AutoCompleteExtender 控制項。

圖 4:使用 AutoCompleteExtender 控制項。 (按一下即可檢視完整大小的影像)

將網站設定為使用 AJAX 工具組 ASP.NET 之後,就可以將 AutoCompleteExtender 控制項新增至頁面中,就像新增一般 ASP.NET 伺服器控制項一樣。 清單 19 顯示使用 控制項呼叫 Web 服務的範例。

清單 19. 使用 ASP.NET AJAX Toolkit AutoCompleteExtender 控制項。

<ajaxToolkit:AutoCompleteExtender ID="extTxtCustomerID" runat="server"
     MinimumPrefixLength="1" ServiceMethod="GetCustomerIDs"
     ServicePath="~/CustomersService.asmx"
     TargetControlID="txtCustomerID" />

AutoCompleteExtender 有數個不同的屬性,包括伺服器控制項上找到的標準識別碼和 Runat 屬性。 除了這些字元之外,還可讓您定義使用者輸入多少字元,再查詢 Web 服務以取得資料。 List 19 中顯示的 MinimumPrefixLength 屬性會導致每次在文字方塊中輸入字元時呼叫服務。 您會想要謹慎設定此值,因為每次使用者輸入字元時,都會呼叫 Web 服務來搜尋符合文字方塊中字元的值。 要呼叫的 Web 服務以及目標 Web 方法分別使用 ServicePath 和 ServiceMethod 屬性來定義。 最後,TargetControlID 屬性會識別要連結 AutoCompleteExtender 控制項的文字方塊。

呼叫的 Web 服務必須套用 ScriptService 屬性,如先前所述,而且目標 Web 方法必須接受兩個名為 prefixText 和 count 的參數。 prefixText 參數代表使用者輸入的字元,而 count 參數代表要傳回的專案數, (預設值為 10) 。 清單 20 顯示先前在清單 19 中顯示的 AutoCompleteExtender 控制項所呼叫的 GetCustomerIDs Web 方法範例。 Web 方法會呼叫商務層方法,接著會呼叫處理篩選資料並傳回相符結果的資料層方法。 資料層方法的程式碼會顯示在清單 21 中。

清單 20。 篩選從 AutoCompleteExtender 控制項傳送的資料。

[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count) 
{
     return Biz.BAL.GetCustomerIDs(prefixText, count);
}

清單 21。 根據使用者輸入篩選結果。

public static string[] GetCustomerIDs(string prefixText, int count)
{
     //Customer IDs cached in _CustomerIDs field to improve performance
     if (_CustomerIDs == null)
     {
          List<string> ids = new List<string>();
          //SQL text used for simplicity...recommend using sprocs
          string sql = "SELECT CustomerID FROM Customers";
          DbConnection conn = GetDBConnection();
          conn.Open();
          DbCommand cmd = conn.CreateCommand();
          cmd.CommandText = sql;
          DbDataReader reader = cmd.ExecuteReader();
          while (reader.Read())
          {
               ids.Add(reader["CustomerID"].ToString());
          }
          reader.Close();
          conn.Close();
          _CustomerIDs = ids.ToArray();
     }
     int index = Array.BinarySearch(_CustomerIDs, prefixText, new CaseInsensitiveComparer());
     //~ is bitwise complement (reverse each bit)
     if (index < 0) index = ~index;
     int matchingCount;
     for (matchingCount = 0; matchingCount < count && index + matchingCount < _CustomerIDs.Length; matchingCount++)
     {
          if (!_CustomerIDs[index + matchingCount].StartsWith(prefixText, StringComparison.CurrentCultureIgnoreCase))
          {
               break;
          }
     }
     String[] returnValue = new string[matchingCount];
     if (matchingCount > 0)
     {
          Array.Copy(_CustomerIDs, index, returnValue, 0, matchingCount);
     }
     return returnValue;
}

結論

ASP.NET AJAX 提供絕佳的呼叫 Web 服務支援,而不需要撰寫許多自訂 JavaScript 程式碼來處理要求和回應訊息。 在本文中,您已瞭解如何使用 AJAX 啟用 .NET Web 服務,讓他們能夠處理 JSON 訊息,以及如何使用 ScriptManager 控制項定義 JavaScript Proxy。 您也已瞭解如何使用 JavaScript Proxy 來呼叫 Web 服務、處理簡單且複雜的類型,以及處理失敗。 最後,您已瞭解如何使用頁面方法來簡化建立和發出 Web 服務呼叫的程式,以及 AutoCompleteExtender 控制項如何在使用者輸入時提供協助。 雖然 ASP.NET AJAX 中提供的 UpdatePanel 當然是許多 AJAX 程式設計人員的選擇控制權,但其簡單性,但瞭解如何透過 JavaScript Proxy 呼叫 Web 服務,在許多應用程式中都很有用。

簡歷

Dan Wahlin (Microsoft ASP.NET 和 XML Web Services) 最有價值的專業版是介面技術訓練 () http://www.interfacett.com 的 .NET 開發講師和架構顧問。 Dan 所建立的 XML for ASP.NET Developers 網站 (www.XMLforASP.NET) ,位於 INETA 演講者的局,並在數個會議中演講。 Dan 共同撰寫專業 Windows DNA (Wrox) ,ASP.NET:秘訣、教學課程和程式碼 (Sams) 、ASP.NET 1.1 測試人員解決方案、專業版 ASP.NET 2.0 AJAX (Wrox) 、ASP.NET 2.0 MVP Hacks,並針對 ASP.NET 開發人員 (Sams) 撰寫 XML。 當他未撰寫程式碼、文章或書籍時,Dan 喜歡撰寫和錄製音樂,以及與他的快樂和兒童一起玩運動和籃球。

Scott Cate 自 1997 年以來一直與 Microsoft Web 技術合作,而且是 myKB.com (www.myKB.com) ,他專門撰寫著重于知識庫軟體解決方案的 ASP.NET 型應用程式。 Scott 可以透過電子郵件連絡, scott.cate@myKB.com 或其部落格位於 ScottCate.com