共用方式為


了解 ASP.NET AJAX Web 服務

斯科特·凱特

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

使用 ASP.NET AJAX 呼叫 Web 服務

Dan Wahlin

Web 服務是 .NET Framework 不可或缺的一部分,可提供跨平台解決方案,在分散式系統之間交換數據。 雖然 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 Web 服務 Proxy 從 ASP.NET AJAX 頁面到 .NET Web 服務的 JavaScript 物件表示法 (JSON) 呼叫。 ASP.NET AJAX 會將 JSON 訊息傳送至 Web 服務,而不是通常與 Web 服務相關聯的標準簡單物件存取通訊協定 (SOAP) 呼叫。 這會導致整體要求和回應訊息較小。 它也允許更有效率的用戶端處理數據,因為 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 的 Web 服務

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

無論您是否不熟悉 ASP.NET Web 服務,您都會發現建立和啟用 AJAX 的服務相當簡單。 自 2002 年初始版本以來,.NET Framework 支援建立 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 Extension 的 ScriptService 屬性套用至 Web 服務類別 ASP.NET。 這可讓 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 屬性是唯一必須在 .NET Web 服務中定義的 ASP.NET AJAX 屬性,以便 ASP.NET AJAX 頁面使用。 不過,另一個名為 ScriptMethod 的屬性也可以直接套用至服務中的 Web 方法。 ScriptMethod 定義三個屬性,包括 UseHttpGetXmlSerializeStringResponseFormat 。 當 Web 方法接受的要求類型必須變更為 GET 時,當 Web 方法需要以 或 XmlElement 物件的形式XmlDocument傳回原始 XML 數據,或從服務傳回的數據應該一律串行化為 XML,而不是 JSON 時,變更這些屬性的值可能會很有用。

當 Web 方法應該接受 GET 要求,而不是 POST 要求時,可以使用 UseHttpGet 屬性。 要求會使用 URL 與轉換成 QueryString 參數的 Web 方法輸入參數來傳送。 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 屬性有 XmlSerializeString 屬性的值 ResponseFormat.Json 會被忽略。

清單 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 訊息。 您可以使用 AJAX ScriptManager 控制件 ASP.NET 自動產生 JavaScript Proxy。

使用 ScriptManager 的 Services 屬性,建立可呼叫 Web 服務的 JavaScript Proxy。 這個屬性可讓您定義 ASP.NET AJAX 頁面可以異步呼叫的一或多個服務,以傳送或接收數據,而不需要回傳作業。 您可以使用 AJAX ServiceReference 控制件 ASP.NET 定義服務,並將 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 服務的參數都可以與處理傳回數據的回呼函式一起定義。

使用 JavaScript Proxy 呼叫名為 GetCustomersByCountry() 的 Web 方法範例會顯示在清單 13 中。 當使用者按下頁面上的按鈕時,會呼叫 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 會處理從服務傳回的 Customer 物件數位,並將其轉換成頁面中顯示的數據表。 從呼叫產生的輸出會顯示在圖 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() 回呼函式,以接受物件表示錯誤做為參數。 error 物件會公開數個不同的函式,以判斷錯誤的原因,以及呼叫是否逾時。清單 14 顯示使用不同錯誤函式的範例,圖 2 顯示函式所產生的輸出範例。

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

圖 2:呼叫 AJAX 錯誤函式 ASP.NET 所產生的輸出。 (點擊查看完整圖片)

處理從 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 物件非常類似。 您必須先指定應該傳遞至page方法的所有參數數據,然後定義異步呼叫傳回時應該呼叫的回呼函式。 您也可以指定失敗回呼(如需處理失敗的範例,請參閱清單 14)。

AutoCompleteExtender 和 ASP.NET AJAX 工具組

ASP.NET AJAX 工具組(可從 https://github.com/DevExpress/AjaxControlToolkit取得)提供數個控件,可用來存取 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 控件。 (點擊查看完整圖片)

一旦網站設定為使用 ASP.NET AJAX Toolkit,就可以將 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服務以及目標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 創立了適用於 ASP.NET 開發人員網站的 XML(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 駭客,併為 ASP.NET 開發人員撰寫 XML(Sams)。 當他不寫程式代碼、文章或書籍時,Dan 喜歡寫和錄製音樂,和他的妻子和孩子一起打高爾夫球和籃球。

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