Grundlegendes zu Webdiensten von ASP.NET AJAX
von Scott Cate
Webdienste sind ein integraler Bestandteil des .NET Frameworks, das eine plattformübergreifende Lösung für den Austausch von Daten zwischen verteilten Systemen bereitstellt. Obwohl Webdienste normalerweise verwendet werden, um verschiedenen Betriebssystemen, Objektmodellen und Programmiersprachen das Senden und Empfangen von Daten zu ermöglichen, können sie auch verwendet werden, um Daten dynamisch in eine ASP.NET AJAX-Seite einzufügen oder Daten von einer Seite an ein Back-End-System zu senden. All dies kann erfolgen, ohne auf Postbackvorgänge zurückgreifen zu müssen.
Aufrufen von Webdiensten mit ASP.NET AJAX
Dan Wahlin
Webdienste sind ein integraler Bestandteil des .NET Frameworks, das eine plattformübergreifende Lösung für den Austausch von Daten zwischen verteilten Systemen bereitstellt. Obwohl Webdienste normalerweise verwendet werden, um verschiedenen Betriebssystemen, Objektmodellen und Programmiersprachen das Senden und Empfangen von Daten zu ermöglichen, können sie auch verwendet werden, um Daten dynamisch in eine ASP.NET AJAX-Seite einzufügen oder Daten von einer Seite an ein Back-End-System zu senden. All dies kann erfolgen, ohne auf Postbackvorgänge zurückgreifen zu müssen.
Während das ASP.NET AJAX UpdatePanel-Steuerelement eine einfache Möglichkeit bietet AJAX jede ASP.NET Seite zu aktivieren, kann es vorkommen, dass Sie dynamisch auf Daten auf dem Server zugreifen müssen, ohne ein UpdatePanel zu verwenden. In diesem Artikel erfahren Sie, wie Sie dies erreichen können, indem Sie Webdienste innerhalb ASP.NET AJAX-Seiten erstellen und verwenden.
Dieser Artikel konzentriert sich auf die Funktionalität, die im Kern ASP.NET AJAX-Erweiterungen verfügbar ist, sowie auf ein Webdienst-fähiges Steuerelement im ASP.NET AJAX Toolkit namens AutoCompleteExtender. Die behandelten Themen umfassen das Definieren von AJAX-fähigen Webdiensten, das Erstellen von Clientproxys und das Aufrufen von Webdiensten mit JavaScript. Außerdem erfahren Sie, wie Webdienstaufrufe direkt an ASP.NET Seitenmethoden vorgenommen werden können.
Webdienstkonfiguration
Wenn ein neues Websiteprojekt mit Visual Studio 2008 erstellt wird, verfügt die Datei "web.config" über eine Reihe neuer Ergänzungen, die Benutzern früherer Versionen von Visual Studio möglicherweise nicht vertraut sind. Einige dieser Änderungen ordnen das Präfix "asp" ASP.NET AJAX-Steuerelementen zu, sodass sie auf Seiten verwendet werden können, während andere erforderliche HttpHandler und HttpModule definieren. Eintrag 1 zeigt Änderungen an dem <httpHandlers>
Element in "web.config", die sich auf Webdienstaufrufe auswirken. Der zum Verarbeiten von ASMX-Aufrufen verwendete HttpHandler wird entfernt und durch eine ScriptHandlerFactory-Klasse ersetzt, die sich in der System.Web.Extensions.dll-Assembly befindet. System.Web.Extensions.dll enthält alle kernigen Funktionen, die von ASP.NET AJAX verwendet werden.
Auflistung 1: ASP.NET AJAX-Webdiensthandlerkonfiguration
<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>
Dieser HttpHandler-Ersatz wird vorgenommen, damit JavaScript Object Notation (JSON)-Aufrufe von ASP.NET AJAX-Seiten mithilfe eines JavaScript-Webdienstproxys an .NET-Webdienste gesendet werden können. ASP.NET AJAX sendet JSON-Nachrichten an Webdienste im Gegensatz zu den standardmäßigen SOAP-Aufrufen (Simple Object Access Protocol), die in der Regel mit Webdiensten verknüpft sind. Dies führt insgesamt zu kleineren Anforderungs- und Antwortnachrichten. Es ermöglicht auch eine effizientere clientseitige Verarbeitung von Daten, da die ASP.NET AJAX JavaScript-Bibliothek für die Arbeit mit JSON-Objekten optimiert ist. Die Auflistung 2 und Eintrag 3 zeigt Beispiele für Webdienstanforderungs- und Antwortnachrichten, die in das JSON-Format serialisiert wurden. Die in Listing 2 angezeigte Anforderungsnachricht übergibt einen Länderparameter mit dem Wert "Belgien", während die Antwortnachricht in Listing 3 ein Array von Kundenobjekten und den zugehörigen Eigenschaften übergibt.
Eintrag 2. In JSON serialisierte Webdienstanforderungsnachricht
{"country":"Belgium"}
> [! HINWEIS] Der Vorgangsname wird als Teil der URL für den Webdienst definiert; Darüber hinaus werden Anforderungsnachrichten nicht immer über JSON übermittelt. Webdienste können das ScriptMethod-Attribut verwenden, wobei der UseHttpGet-Parameter auf "true" festgelegt ist. Dies bewirkt, dass Parameter über eine Abfragezeichenfolge übergeben werden.
Eintrag 3. In JSON serialisierte Webdienstantwortnachricht
[{"__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"}]
Im nächsten Abschnitt erfahren Sie, wie Sie Webdienste erstellen, die JSON-Anforderungsnachrichten verarbeiten und mit einfachen und komplexen Typen antworten können.
Erstellen von AJAX-fähigen Webdiensten
Das ASP.NET AJAX-Framework bietet verschiedene Möglichkeiten zum Aufrufen von Webdiensten. Sie können das AutoCompleteExtender-Steuerelement (verfügbar im ASP.NET AJAX Toolkit) oder JavaScript verwenden. Bevor Sie jedoch einen Dienst aufrufen, müssen Sie ihn AJAX aktivieren, damit er von Clientskriptcode aufgerufen werden kann.
Unabhängig davon, ob Sie noch keine Neuerungen bei ASP.NET Webdiensten haben, ist es einfach, Dienste mit AJAX-Aktivierung zu erstellen und zu aktivieren. Das .NET Framework hat die Erstellung von ASP.NET Webdiensten seit seiner ersten Version im Jahr 2002 unterstützt, und die ASP.NET AJAX-Erweiterungen bieten zusätzliche AJAX-Funktionen, die auf den Standardfeatures von .NET Framework aufbauen. Visual Studio .NET 2008 Beta 2 bietet integrierte Unterstützung für das Erstellen von ASMX-Webdienstdateien und leitet automatisch zugeordneten Code neben Klassen von der System.Web.Services.WebService-Klasse ab. Wenn Sie der Klasse Methoden hinzufügen, müssen Sie das WebMethod-Attribut anwenden, damit sie von Webdienst-Consumern aufgerufen werden können.
Listing 4 zeigt ein Beispiel für das Anwenden des WebMethod-Attributs auf eine Methode mit dem Namen GetCustomersByCountry().
Eintrag 4. Verwenden des WebMethod-Attributs in einem Webdienst
[WebMethod]
public Customer[] GetCustomersByCountry(string country)
{
return Biz.BAL.GetCustomersByCountry(country);
}
Die GetCustomersByCountry()-Methode akzeptiert einen Länderparameter und gibt ein Customer-Objektarray zurück. Der an die Methode übergebene Länderwert wird an eine Geschäftsschichtklasse weitergeleitet, die wiederum eine Datenschichtklasse aufruft, um die Daten aus der Datenbank abzurufen, die Eigenschaften des Customer-Objekts mit Daten auszufüllen und das Array zurückzugeben.
Verwenden des ScriptService-Attributs
Beim Hinzufügen des WebMethod-Attributs kann die GetCustomersByCountry()-Methode von Clients aufgerufen werden, die Standard-SOAP-Nachrichten an den Webdienst senden, es lässt jedoch nicht zu, dass JSON-Aufrufe aus ASP.NET AJAX-Anwendungen außerhalb des Felds erfolgen. Damit JSON-Aufrufe erfolgen können, müssen Sie das Attribut der ASP.NET AJAX-Erweiterung ScriptService
auf die Webdienstklasse anwenden. Auf diese Weise kann ein Webdienst Antwortnachrichten senden, die mit JSON formatiert sind, und das clientseitige Skript ermöglicht das Aufrufen eines Diensts durch Senden von JSON-Nachrichten.
Eintrag 5 zeigt ein Beispiel für das Anwenden des ScriptService-Attributs auf eine Webdienstklasse namens CustomersService.
Eintrag 5. Verwenden des ScriptService-Attributs zum AJAX-Aktivieren eines Webdiensts
[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);
}
}
Das ScriptService-Attribut fungiert als Markierung, die angibt, dass es aus AJAX-Skriptcode aufgerufen werden kann. Es behandelt tatsächlich keine der JSON-Serialisierungs- oder Deserialisierungsaufgaben, die hinter den Kulissen auftreten. Die ScriptHandlerFactory (konfiguriert in web.config) und andere verwandte Klassen führen den Großteil der JSON-Verarbeitung durch.
Verwenden des ScriptMethod-Attributs
Das ScriptService-Attribut ist das einzige ASP.NET AJAX-Attribut, das in einem .NET-Webdienst definiert werden muss, damit es von ASP.NET AJAX-Seiten verwendet werden kann. Ein anderes Attribut namens ScriptMethod kann jedoch auch direkt auf Webmethoden in einem Dienst angewendet werden. ScriptMethod definiert drei Eigenschaften, einschließlich UseHttpGet
, ResponseFormat
und XmlSerializeString
. Das Ändern der Werte dieser Eigenschaften kann in Fällen hilfreich sein, in denen der von einer Webmethode akzeptierte Anforderungstyp in GET geändert werden muss, wenn eine Webmethode unformatierte XML-Daten in Form eines XmlDocument
Oder Objekts zurückgeben muss oder XmlElement
wenn daten, die von einem Dienst zurückgegeben werden, immer als XML statt als JSON serialisiert werden sollen.
Die UseHttpGet-Eigenschaft kann verwendet werden, wenn eine Webmethode GET-Anforderungen im Gegensatz zu POST-Anforderungen akzeptieren soll. Anforderungen werden mithilfe einer URL mit Webmethodeneingabeparametern gesendet, die in QueryString-Parameter konvertiert werden. The UseHttpGet property defaults to false and should only be set to true
when operations are known to be safe and when sensitive data is not passed to a Web Service. Listing 6 zeigt ein Beispiel für die Verwendung des ScriptMethod-Attributs mit der UseHttpGet-Eigenschaft.
Eintrag 6. Verwenden des ScriptMethod-Attributs mit der UseHttpGet-Eigenschaft.
[WebMethod]
[ScriptMethod(UseHttpGet = true)]
public string HttpGetEcho(string input)
{
return input;
}
Ein Beispiel für die Header, die gesendet werden, wenn die in Listing 6 gezeigte HttpGetEcho-Webmethode aufgerufen wird, werden als nächstes angezeigt:
GET /CustomerViewer/DemoService.asmx/HttpGetEcho?input=%22Input Value%22 HTTP/1.1
Zusätzlich dazu, dass Webmethoden HTTP GET-Anforderungen akzeptieren können, kann das ScriptMethod-Attribut auch verwendet werden, wenn XML-Antworten von einem Dienst und nicht aus JSON zurückgegeben werden müssen. Beispielsweise kann ein Webdienst einen RSS-Feed von einer Remotewebsite abrufen und als XmlDocument- oder XmlElement-Objekt zurückgeben. Die Verarbeitung der XML-Daten kann dann auf dem Client erfolgen.
Listing 7 zeigt ein Beispiel für die Verwendung der ResponseFormat-Eigenschaft, um anzugeben, dass XML-Daten von einer Webmethode zurückgegeben werden sollen.
Eintrag 7. Verwenden des ScriptMethod-Attributs mit der ResponseFormat-Eigenschaft.
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlElement GetRssFeed(string url)
{
XmlDocument doc = new XmlDocument();
doc.Load(url);
return doc.DocumentElement;
}
Die ResponseFormat-Eigenschaft kann auch zusammen mit der XmlSerializeString-Eigenschaft verwendet werden. Die XmlSerializeString-Eigenschaft hat einen Standardwert von false, was bedeutet, dass alle Rückgabetypen außer von einer Webmethode zurückgegebenen Zeichenfolgen als XML serialisiert werden, wenn die ResponseFormat
Eigenschaft auf ResponseFormat.Xml
". Wenn XmlSerializeString
dieser Wert festgelegt true
ist, werden alle von einer Webmethode zurückgegebenen Typen als XML serialisiert, einschließlich Zeichenfolgentypen. Wenn die ResponseFormat-Eigenschaft einen Wert der ResponseFormat.Json
XmlSerializeString-Eigenschaft aufweist, wird ignoriert.
Listing 8 zeigt ein Beispiel für die Verwendung der XmlSerializeString-Eigenschaft, um zu erzwingen, dass Zeichenfolgen als XML serialisiert werden.
Eintrag 8. Verwenden des ScriptMethod-Attributs mit der XmlSerializeString-Eigenschaft
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml,XmlSerializeString=true)]
public string GetXmlString(string input)
{
return input;
}
Der wert, der vom Aufrufen der getXmlString-Webmethode zurückgegeben wird, die in Listing 8 angezeigt wird, wird als nächstes angezeigt:
<?xml version="1.0"?>
<string>Test</string>
Obwohl das standardmäßige JSON-Format die Gesamtgröße von Anforderungs- und Antwortnachrichten minimiert und von ASP.NET AJAX-Clients auf browserübergreifende Weise genutzt wird, können die Eigenschaften ResponseFormat und XmlSerializeString verwendet werden, wenn Clientanwendungen wie Internet Explorer 5 oder höher xml-Daten von einer Webmethode zurückgegeben werden.
Arbeiten mit komplexen Typen
Eintrag 5 zeigt ein Beispiel für die Rückgabe eines komplexen Typs namens "Kunde" von einem Webdienst. Die Customer-Klasse definiert mehrere verschiedene einfache Typen intern als Eigenschaften wie "FirstName" und "LastName". Komplexe Typen, die als Eingabeparameter oder Rückgabetyp für eine AJAX-fähige Webmethode verwendet werden, werden automatisch in JSON serialisiert, bevor sie an die clientseitige Methode gesendet werden. Geschachtelte komplexe Typen (die intern innerhalb eines anderen Typs definiert sind) werden dem Client jedoch standardmäßig nicht als eigenständige Objekte zur Verfügung gestellt.
In Fällen, in denen ein geschachtelter komplexer Typ, der von einem Webdienst verwendet wird, auch auf einer Clientseite verwendet werden muss, kann das ASP.NET AJAX GenerateScriptType-Attribut dem Webdienst hinzugefügt werden. Die in Listing 9 gezeigte CustomerDetails-Klasse enthält beispielsweise Adress- und Gender-Eigenschaften, die geschachtelte komplexe Typen darstellen.
Eintrag 9. Die hier gezeigte CustomerDetails-Klasse enthält zwei geschachtelte komplexe Typen.
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; }
}
}
Die in Der Auflistung 9 gezeigten CustomerDetails-Klasse definierten Address- und Gender-Objekte werden nicht automatisch für die Verwendung auf clientseitiger Seite über JavaScript verfügbar gemacht, da sie geschachtelte Typen sind (Address ist eine Klasse und Gender ist eine Enumeration). In Situationen, in denen ein geschachtelter Typ, der in einem Webdienst verwendet wird, auf clientseitiger Seite verfügbar sein muss, kann das zuvor erwähnte GenerateScriptType-Attribut verwendet werden (siehe Eintrag 10). Dieses Attribut kann mehrmals hinzugefügt werden, wenn verschiedene geschachtelte komplexe Typen von einem Dienst zurückgegeben werden. Sie kann direkt auf die Webdienstklasse oder oberhalb bestimmter Webmethoden angewendet werden.
Eintrag 10. Verwenden des GenerateScriptService-Attributs zum Definieren geschachtelter Typen, die für den Client verfügbar sein sollen.
[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
}
Durch Anwenden des GenerateScriptType
Attributs auf den Webdienst werden die Typen "Adresse" und "Geschlecht" automatisch für die Verwendung durch clientseitigen ASP.NET AJAX-JavaScript-Code verfügbar gemacht. Ein Beispiel für javaScript, das automatisch generiert und an den Client gesendet wird, indem das GenerateScriptType-Attribut zu einem Webdienst hinzugefügt wird, wird in Listing 11 angezeigt. Sie erfahren, wie Sie geschachtelte komplexe Typen weiter unten im Artikel verwenden.
Eintrag 11. Geschachtelte komplexe Typen, die einer ASP.NET AJAX-Seite zur Verfügung gestellt werden.
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);
Nachdem Sie nun gesehen haben, wie Sie Webdienste erstellen und für ASP.NET AJAX-Seiten zugänglich machen, sehen wir uns an, wie JavaScript-Proxys erstellt und verwendet werden, damit Daten an Webdienste abgerufen oder an Webdienste gesendet werden können.
Erstellen von JavaScript-Proxys
Das Aufrufen eines Standardwebdiensts (.NET oder einer anderen Plattform) umfasst in der Regel das Erstellen eines Proxyobjekts, das Sie vor den Komplexitäten des Sendens von SOAP-Anforderungs- und Antwortnachrichten schützt. Mit ASP.NET AJAX-Webdienstaufrufen können JavaScript-Proxys erstellt und verwendet werden, um Dienste auf einfache Weise aufzurufen, ohne sich gedankenlos um die Serialisierung und Deserialisierung von JSON-Nachrichten zu kümmern. JavaScript-Proxys können automatisch mithilfe des ASP.NET AJAX ScriptManager-Steuerelements generiert werden.
Das Erstellen eines JavaScript-Proxys, der Webdienste aufrufen kann, erfolgt mithilfe der Eigenschaft ScriptManager Services. Mit dieser Eigenschaft können Sie einen oder mehrere Dienste definieren, die eine ASP.NET AJAX-Seite asynchron aufrufen kann, um Daten zu senden oder zu empfangen, ohne dass Postbackvorgänge erforderlich sind. Sie definieren einen Dienst mithilfe des ASP.NET AJAX-Steuerelements ServiceReference
und Zuweisen der Webdienst-URL zur Eigenschaft des Steuerelements Path
. Eintrag 12 zeigt ein Beispiel für den Verweis auf einen Dienst namens CustomersService.asmx.
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/CustomersService.asmx" />
</Services>
</asp:ScriptManager>
Eintrag 12. Definieren eines Webdiensts, der in einer ASP.NET AJAX-Seite verwendet wird.
Das Hinzufügen eines Verweises auf "CustomersService.asmx" über das ScriptManager-Steuerelement bewirkt, dass ein JavaScript-Proxy dynamisch generiert und von der Seite referenziert wird. Der Proxy wird mithilfe des <Skripttags> eingebettet und dynamisch geladen, indem die Datei CustomersService.asmx aufgerufen und /js an das Ende angefügt wird. Das folgende Beispiel zeigt, wie der JavaScript-Proxy auf der Seite eingebettet ist, wenn das Debuggen in "web.config" deaktiviert ist:
<script src="CustomersService.asmx/js" type="text/javascript"></script>
> [! HINWEIS] Wenn Sie den tatsächlich generierten JavaScript-Proxycode sehen möchten, können Sie die URL zum gewünschten .NET-Webdienst in das Adressfeld von Internet Explorer eingeben und /js an das Ende anfügen.
Wenn das Debuggen in web.config aktiviert ist, wird eine Debugversion des JavaScript-Proxys wie folgt in die Seite eingebettet:
<script src="CustomersService.asmx/jsdebug" type="text/javascript"></script>
Der vom ScriptManager erstellte JavaScript-Proxy kann auch direkt in die Seite eingebettet werden, anstatt mithilfe des <Skripttags-Attributs> zu referenzieren. Dazu können Sie die InlineScript-Eigenschaft des ServiceReference-Steuerelements auf "true" festlegen (der Standardwert ist "false"). Dies kann nützlich sein, wenn ein Proxy nicht über mehrere Seiten hinweg freigegeben wird, und wenn Sie die Anzahl der Netzwerkaufrufe verringern möchten, die an den Server getätigt wurden. Wenn InlineScript auf "true" festgelegt ist, wird das Proxyskript nicht vom Browser zwischengespeichert, sodass der Standardwert "false" in Fällen empfohlen wird, in denen der Proxy von mehreren Seiten in einer ASP.NET AJAX-Anwendung verwendet wird. Nachfolgend sehen Sie ein Beispiel für die Verwendung der InlineScript-Eigenschaft:
<asp:ServiceReference InlineScript="true" Path="~/CustomersService.asmx"/>
Verwenden von JavaScript-Proxys
Sobald ein Webdienst von einer ASP.NET AJAX-Seite mithilfe des ScriptManager-Steuerelements referenziert wird, kann ein Aufruf an den Webdienst erfolgen, und die zurückgegebenen Daten können mithilfe von Rückruffunktionen verarbeitet werden. Ein Webdienst wird aufgerufen, indem auf den Namespace (sofern vorhanden), der Klassenname und der Webmethodenname verwiesen wird. Alle parameter, die an den Webdienst übergeben werden, können zusammen mit einer Rückruffunktion definiert werden, die die zurückgegebenen Daten verarbeitet.
Ein Beispiel für die Verwendung eines JavaScript-Proxys zum Aufrufen einer Webmethode mit dem Namen "GetCustomersByCountry()" ist in Listing 13 dargestellt. Die GetCustomersByCountry()-Funktion wird aufgerufen, wenn ein Endbenutzer auf eine Schaltfläche auf der Seite klickt.
Eintrag 13. Aufrufen eines Webdiensts mit einem JavaScript-Proxy.
function GetCustomerByCountry()
{
var country = $get("txtCountry").value;
InterfaceTraining.CustomersService.GetCustomersByCountry(country, OnWSRequestComplete);
}
function OnWSRequestComplete(results)
{
if (results != null)
{
CreateCustomersTable(results);
GetMap(results);
}
}
Dieser Aufruf verweist auf den InterfaceTraining-Namespace, die CustomersService-Klasse und die im Dienst definierte GetCustomersByCountry-Webmethode. Er übergibt einen Länderwert, der aus einem Textfeld abgerufen wird, sowie eine Rückruffunktion namens OnWSRequestComplete, die aufgerufen werden soll, wenn der asynchrone Webdienstaufruf zurückgegeben wird. OnWSRequestComplete behandelt das Array von Kundenobjekten, die vom Dienst zurückgegeben werden, und konvertiert sie in eine Tabelle, die auf der Seite angezeigt wird. Die aus dem Anruf generierte Ausgabe wird in Abbildung 1 dargestellt.
Abbildung 1: Binden von Daten, die durch einen asynchronen AJAX-Aufruf an einen Webdienst abgerufen werden. (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
JavaScript-Proxys können auch Unidirektionale Aufrufe an Webdienste vornehmen, wenn eine Webmethode aufgerufen werden soll, der Proxy sollte jedoch nicht auf eine Antwort warten. Sie können z. B. einen Webdienst aufrufen, um einen Prozess wie einen Arbeitsablauf zu starten, aber nicht auf einen Rückgabewert vom Dienst warten. In Fällen, in denen ein Unidirektionale Aufruf an einen Dienst erfolgen muss, kann die in Listing 13 gezeigte Rückruffunktion einfach weggelassen werden. Da keine Rückruffunktion definiert ist, wartet das Proxyobjekt nicht, bis der Webdienst Daten zurückgibt.
Behandeln von Fehlern
Asynchrone Rückrufe an Webdienste können auf verschiedene Arten von Fehlern stoßen, z. B. das Netzwerk, das nicht verfügbar ist oder eine Ausnahme zurückgegeben wird. Glücklicherweise ermöglichen javaScript-Proxyobjekte, die vom ScriptManager generiert werden, mehrere Rückrufe zu definieren, um Fehler und Fehler zusätzlich zu den zuvor gezeigten Erfolgsrückrufen zu behandeln. Eine Fehlerrückruffunktion kann unmittelbar nach der Standardrückruffunktion im Aufruf der Webmethode definiert werden, wie in Listing 14 dargestellt.
Eintrag 14. Definieren einer Fehlerrückruffunktion und Anzeigen von Fehlern
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());
}
Alle Fehler, die auftreten, wenn der Webdienst aufgerufen wird, lösen die OnWSRequestFailed()-Rückruffunktion aus, die aufgerufen wird, wodurch ein Objekt akzeptiert wird, das den Fehler als Parameter darstellt. Das Fehlerobjekt macht mehrere unterschiedliche Funktionen verfügbar, um die Ursache des Fehlers zu bestimmen und zu bestimmen, ob der Aufruf timeout beendet wurde. Die Auflistung 14 zeigt ein Beispiel für die Verwendung der verschiedenen Fehlerfunktionen und Abbildung 2 zeigt ein Beispiel für die ausgabe, die von den Funktionen generiert wird.
Abbildung 2: Durch Aufrufen ASP.NET AJAX-Fehlerfunktionen generierte Ausgabe. (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Behandeln von XML-Daten, die von einem Webdienst zurückgegeben werden
Früher haben Sie gesehen, wie eine Webmethode unformatierte XML-Daten mithilfe des ScriptMethod-Attributs zusammen mit der ResponseFormat-Eigenschaft zurückgeben konnte. Wenn "ResponseFormat" auf "ResponseFormat.Xml" festgelegt ist, werden die vom Webdienst zurückgegebenen Daten nicht als JSON, sondern als XML serialisiert. Dies kann hilfreich sein, wenn XML-Daten direkt an den Client zur Verarbeitung mit JavaScript oder XSLT übergeben werden müssen. Derzeit bietet Internet Explorer 5 oder höher das beste clientseitige Objektmodell zum Analysieren und Filtern von XML-Daten aufgrund der integrierten Unterstützung für MSXML.
Das Abrufen von XML-Daten aus einem Webdienst unterscheidet sich nicht vom Abrufen anderer Datentypen. Rufen Sie zunächst den JavaScript-Proxy auf, um die entsprechende Funktion aufzurufen und eine Rückruffunktion zu definieren. Sobald der Aufruf zurückgegeben wird, können Sie die Daten in der Rückruffunktion verarbeiten.
Listing 15 zeigt ein Beispiel für den Aufruf einer Webmethode mit dem Namen GetRssFeed(), die ein XmlElement-Objekt zurückgibt. GetRssFeed() akzeptiert einen einzelnen Parameter, der die URL für den ABZURUFENden RSS-Feed darstellt.
Eintrag 15. Arbeiten mit XML-Daten, die von einem Webdienst zurückgegeben werden.
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+";
}
}
In diesem Beispiel wird eine URL an einen RSS-Feed übergeben und die zurückgegebenen XML-Daten in der OnWSRequestComplete()-Funktion verarbeitet. OnWSRequestComplete() überprüft zuerst, ob der Browser Internet Explorer ist, um zu wissen, ob der MSXML-Parser verfügbar ist. Wenn ja, wird eine XPath-Anweisung verwendet, um alle <Elementtags> innerhalb des RSS-Feeds zu suchen. Jedes Element wird dann durchlaufen, und die zugehörigen <Titel- und <Linktags> werden gefunden und verarbeitet, um die Daten der> einzelnen Elemente anzuzeigen. Abbildung 3 zeigt ein Beispiel für die Ausgabe, die aus einem ASP.NET AJAX-Aufruf über einen JavaScript-Proxy an die GetRssFeed()-Webmethode generiert wurde.
Behandeln komplexer Typen
Komplexe Typen, die von einem Webdienst akzeptiert oder zurückgegeben werden, werden automatisch über einen JavaScript-Proxy verfügbar gemacht. Geschachtelte komplexe Typen können jedoch nicht direkt auf die clientseitige Seite zugegriffen werden, es sei denn, das GenerateScriptType-Attribut wird wie zuvor beschrieben auf den Dienst angewendet. Warum möchten Sie einen geschachtelten komplexen Typ auf clientseitiger Seite verwenden?
Um diese Frage zu beantworten, gehen Sie davon aus, dass eine ASP.NET AJAX-Seite Kundendaten anzeigt und Endbenutzern das Aktualisieren der Kundenadresse ermöglicht. Wenn der Webdienst angibt, dass der Adresstyp (ein komplexer Typ, der in einer CustomerDetails-Klasse definiert ist) an den Client gesendet werden kann, kann der Aktualisierungsprozess in separate Funktionen unterteilt werden, um die Verwendung von Code zu verbessern.
Abbildung 3: Ausgabe beim Erstellen eines Webdiensts, der RSS-Daten zurückgibt. (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Eintrag 16 zeigt ein Beispiel für clientseitigen Code, der ein in einem Model-Namespace definiertes Address-Objekt aufruft, es mit aktualisierten Daten ausfüllt und der Address-Eigenschaft eines CustomerDetails-Objekts zuweist. Das CustomerDetails -Objekt wird dann zur Verarbeitung an den Webdienst übergeben.
Eintrag 16. Verwenden geschachtelter komplexer Typen
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")+ "!");
}
Erstellen und Verwenden von Seitenmethoden
Webdienste bieten eine hervorragende Möglichkeit, wiederverwendbare Dienste für eine Vielzahl von Clients verfügbar zu machen, einschließlich ASP.NET AJAX-Seiten. Es kann jedoch vorkommen, dass eine Seite Daten abrufen muss, die nicht von anderen Seiten verwendet oder freigegeben werden. In diesem Fall kann das Erstellen einer ASMX-Datei, um der Seite den Zugriff auf die Daten zu ermöglichen, wie überübt erscheinen, da der Dienst nur von einer einzelnen Seite verwendet wird.
ASP.NET AJAX bietet einen weiteren Mechanismus zum Erstellen von Webdienst-ähnlichen Aufrufen, ohne eigenständige ASMX-Dateien zu erstellen. Dazu wird eine Technik verwendet, die als "Seitenmethoden" bezeichnet wird. Seitenmethoden sind statische (in VB.NET) Methoden, die direkt in eine Seite oder Code-neben-Datei eingebettet sind, auf die das WebMethod-Attribut angewendet wurde. Durch Anwenden des WebMethod-Attributs können sie mithilfe eines speziellen JavaScript-Objekts namens PageMethods aufgerufen werden, das dynamisch zur Laufzeit erstellt wird. Das PageMethods-Objekt fungiert als Proxy, der Sie vom JSON-Serialisierungs-/Deserialisierungsprozess abschirmt. Beachten Sie, dass Sie für die Verwendung des PageMethods-Objekts die EnablePageMethods-Eigenschaft von ScriptManager auf "true" festlegen müssen.
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
</asp:ScriptManager>
Listing 17 zeigt ein Beispiel zum Definieren von zwei Seitenmethoden in einer ASP.NET Code-neben-Klasse. Diese Methoden rufen Daten aus einer Unternehmensschichtklasse ab, die sich im ordner App_Code der Website befindet.
Eintrag 17. Definieren von Seitenmethoden.
[WebMethod]
public static Customer[] GetCustomersByCountry(string country)
{
return Biz.BAL.GetCustomersByCountry(country);
}
[WebMethod]
public static Customer[] GetCustomersByID(string id)
{
return Biz.BAL.GetCustomersByID(id);
}
Wenn ScriptManager das Vorhandensein von Webmethoden auf der Seite erkennt, generiert er einen dynamischen Verweis auf das weiter oben erwähnte PageMethods-Objekt. Das Aufrufen einer Webmethode erfolgt durch Verweisen auf die PageMethods-Klasse gefolgt vom Namen der Methode und allen erforderlichen Parameterdaten, die übergeben werden sollen. Listing 18 zeigt Beispiele für das Aufrufen der beiden zuvor gezeigten Seitenmethoden.
Eintrag 18. Aufrufen von Seitenmethoden mit dem PageMethods JavaScript-Objekt.
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);
}
Die Verwendung des PageMethods-Objekts ähnelt der Verwendung eines JavaScript-Proxyobjekts. Sie geben zunächst alle Parameterdaten an, die an die Seitenmethode übergeben werden sollen, und definieren dann die Rückruffunktion, die aufgerufen werden soll, wenn der asynchrone Aufruf zurückgegeben wird. Ein Fehlerrückruf kann auch angegeben werden (siehe Eintrag 14 für ein Beispiel für die Behandlung von Fehlern).
AutoCompleteExtender und das ASP.NET AJAX Toolkit
Das ASP.NET AJAX Toolkit (verfügbar von https://github.com/DevExpress/AjaxControlToolkit) bietet mehrere Steuerelemente, die für den Zugriff auf Webdienste verwendet werden können. Insbesondere enthält das Toolkit ein nützliches Steuerelement mit dem Namen AutoCompleteExtender
, mit dem Webdienste aufgerufen und Daten auf Seiten angezeigt werden können, ohne überhaupt JavaScript-Code zu schreiben.
Das AutoCompleteExtender-Steuerelement kann verwendet werden, um vorhandene Funktionen eines Textfelds zu erweitern und Benutzern das Auffinden von Daten zu erleichtern, die sie suchen. Während der Eingabe in ein Textfeld kann das Steuerelement verwendet werden, um einen Webdienst abzufragen und Ergebnisse unterhalb des Textfelds dynamisch anzuzeigen. Abbildung 4 zeigt ein Beispiel für die Verwendung des AutoCompleteExtender-Steuerelements zum Anzeigen von Kunden-IDs für eine Supportanwendung. Wenn der Benutzer unterschiedliche Zeichen in das Textfeld eingibt, werden verschiedene Elemente basierend auf ihrer Eingabe darunter angezeigt. Benutzer können dann die gewünschte Kunden-ID auswählen.
Die Verwendung des AutoCompleteExtender innerhalb einer ASP.NET AJAX-Seite erfordert, dass die AjaxControlToolkit.dll-Assembly dem Ordner "Bin" der Website hinzugefügt wird. Nachdem die Toolkitassembly hinzugefügt wurde, möchten Sie in "web.config" darauf verweisen, damit die darin enthaltenen Steuerelemente für alle Seiten in einer Anwendung verfügbar sind. Dazu fügen Sie das folgende Tag im Steuerelementtag> "web.config<" hinzu:
<add namespace="AjaxControlToolkit" assembly="AjaxControlToolkit" tagPrefix="ajaxToolkit"/>
In Fällen, in denen Sie das Steuerelement nur auf einer bestimmten Seite verwenden müssen, können Sie darauf verweisen, indem Sie die Verweisdirektive oben auf einer Seite hinzufügen, anstatt web.config zu aktualisieren:
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit"
TagPrefix="ajaxToolkit" %>
Abbildung 4: Verwenden des AutoCompleteExtender-Steuerelements. (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Nachdem die Website für die Verwendung des ASP.NET AJAX Toolkit konfiguriert wurde, kann ein AutoCompleteExtender-Steuerelement auf der Seite hinzugefügt werden, ähnlich wie Sie ein normales ASP.NET Serversteuerelement hinzufügen. Listing 19 zeigt ein Beispiel für die Verwendung des Steuerelements zum Aufrufen eines Webdiensts.
Eintrag 19. Verwenden des ASP.NET AJAX Toolkit AutoCompleteExtender-Steuerelements.
<ajaxToolkit:AutoCompleteExtender ID="extTxtCustomerID" runat="server"
MinimumPrefixLength="1" ServiceMethod="GetCustomerIDs"
ServicePath="~/CustomersService.asmx"
TargetControlID="txtCustomerID" />
Der AutoCompleteExtender verfügt über verschiedene Eigenschaften, einschließlich der Standard-ID- und Runat-Eigenschaften, die auf Serversteuerelementen zu finden sind. Darüber hinaus können Sie definieren, wie viele Zeichen ein Endbenutzer eingibt, bevor der Webdienst nach Daten abgefragt wird. Die in Listing 19 angezeigte MinimumPrefixLength-Eigenschaft bewirkt, dass der Dienst jedes Mal aufgerufen wird, wenn ein Zeichen in das Textfeld eingegeben wird. Sie sollten diesen Wert sorgfältig festlegen, da der Benutzer jedes Mal, wenn der Benutzer ein Zeichen eingibt, aufgerufen wird, um nach Werten zu suchen, die den Zeichen im Textfeld entsprechen. Der Webdienst, der aufgerufen werden soll, sowie die Zielwebmethode werden mit den Eigenschaften ServicePath und ServiceMethod definiert. Schließlich identifiziert die TargetControlID-Eigenschaft, mit welchem Textfeld das AutoCompleteExtender-Steuerelement eingebunden werden soll.
Der aufgerufene Webdienst muss das ScriptService-Attribut wie zuvor beschrieben angewendet haben, und die Zielwebmethode muss zwei Parameter mit dem Namen "prefixText" und "count" akzeptieren. Der parameter prefixText stellt die vom Endbenutzer eingegebenen Zeichen dar, und der Count-Parameter stellt dar, wie viele Elemente zurückgegeben werden sollen (der Standardwert ist 10). Listing 20 zeigt ein Beispiel für die GetCustomerIDs-Webmethode, die vom AutoCompleteExtender-Steuerelement aufgerufen wird, das weiter oben in Listing 19 gezeigt wird. Die Webmethode ruft eine Geschäftsschichtmethode auf, die wiederum eine Datenschichtmethode aufruft, die das Filtern der Daten verarbeitet und die übereinstimmenden Ergebnisse zurückgibt. Der Code für die Datenschichtmethode wird in Listing 21 angezeigt.
Eintrag 20. Filtern von Daten, die vom AutoCompleteExtender-Steuerelement gesendet werden.
[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count)
{
return Biz.BAL.GetCustomerIDs(prefixText, count);
}
Eintrag 21. Filtern von Ergebnissen basierend auf der Benutzereingabe.
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;
}
Zusammenfassung
ASP.NET AJAX bietet hervorragende Unterstützung für das Aufrufen von Webdiensten, ohne viele benutzerdefinierten JavaScript-Code zum Verarbeiten der Anforderungs- und Antwortnachrichten zu schreiben. In diesem Artikel haben Sie erfahren, wie Ajax-fähige .NET-Webdienste zum Verarbeiten von JSON-Nachrichten und zum Definieren von JavaScript-Proxys mithilfe des ScriptManager-Steuerelements verwendet werden. Sie haben auch gesehen, wie JavaScript-Proxys verwendet werden können, um Webdienste aufzurufen, einfache und komplexe Typen zu behandeln und Fehler zu behandeln. Schließlich haben Sie gesehen, wie Seitenmethoden verwendet werden können, um den Prozess des Erstellens und Tätigens von Webdienstaufrufen zu vereinfachen und wie das AutoCompleteExtender-Steuerelement Endbenutzern während der Eingabe Hilfe bieten kann. Obwohl das updatePanel in ASP.NET AJAX aufgrund seiner Einfachheit sicher die Kontrolle über die Wahl für viele AJAX-Programmierer sein wird, wissen, wie Webdienste über JavaScript-Proxys aufgerufen werden können, kann in vielen Anwendungen nützlich sein.
Biografie
Dan Wahlin (Microsoft Most Valuable Professional for ASP.NET and XML Web Services) ist ein .NET Development Instructor und Architekturberater bei Interface Technical Training (http://www.interfacett.com). Dan gründete die XML für ASP.NET Developers Web Site (www.XMLforASP.NET), ist im INETA Speaker es Bureau und spricht auf mehreren Konferenzen. Dan co-authored Professional Windows DNA (Wrox), ASP.NET: Tips, Tutorials and Code (Sams), ASP.NET 1.1 Insider Solutions, Professional ASP.NET 2.0 AJAX (Wrox), ASP.NET 2.0 MVP Hacks und authored XML for ASP.NET Developers (Sams). Wenn er nicht Code, Artikel oder Bücher schreibt, schreibt Dan Musik und spielt Golf und Basketball mit seiner Frau und Kindern.
Scott Cate arbeitet seit 1997 mit Microsoft Web Technologies zusammen und ist Präsident von myKB.com (www.myKB.com), wo er sich auf das Schreiben ASP.NET basierten Anwendungen spezialisiert hat, die sich auf Knowledge Base-Softwarelösungen konzentrierten. Scott kann per E-Mail oder seinem Blog bei scott.cate@myKB.com ScottCate.com kontaktiert werden