Traduction

Cet article est la traduction la plus fidèle possible de l'article original : Understanding ASP.NET AJAX Web Services by Dan Wahlin

Appel de Services Web avec ASP.NET AJAX

Les Web Services font partie intégrante du framework .NET et fournissent une solution multi-plateforme pour l'échange de données entre systèmes distribués. Bien que les Web Services soient normalement utilisés pour permettre à différents systèmes d'exploitation, à différents modèles objets et langages de programmation, d'envoyer et recevoir des données, ils peuvent également être utilisés pour injecter dynamiquement des données dans une page ASP.NET AJAX ou pour envoyer des données à partir d'une page vers un système back-end. Tout cela peut se faire sans avoir à recourir à des opérations de postback. Alors que le contrôle UpdatePanel d'ASP.NET AJAX fournit un moyen simple d'ajouter de l'AJAX à toute page ASP.NET, il peut arriver des moments où vous aurez besoin d'accéder dynamiquement aux données sur le serveur sans l'aide d'un UpdatePanel. Dans cet article, vous verrez comment accomplir ceci en créant et en consommant des services Web dans des pages ASP.NET AJAX. Cet article se concentrera sur les fonctionnalités disponibles dans les extensions ASP.NET AJAX ainsi que sur un contrôle de l'ASP.NET AJAX Toolkit capable d'utiliser des Web Services, le contrôle AutoCompleteExtender. Les sujets abordés comprennent la définition d'un web service disponible pour utilisation avec AJAX (AJAX-enabled services Web), la création de proxys et l'appel de services Web avec JavaScript. Vous verrez également comment les appels de service Web peuvent être effectués directement sur des méthodes page ASP.NET.

Configuration des services web

Quand un nouveau projet de site Web est créé avec Visual Studio 2008, le fichier web.config a un certain nombre de nouveaux ajouts qui peuvent être peu familier pour les utilisateurs de versions précédentes de Visual Studio. Certaines de ces modifications ont le préfixe "asp" des contrôles ASP.NET AJAX et peuvent être utilisées dans les pages, tandis que d'autres définissent des handlers HTTP (HttpHandler) et modules HTTP (HttpModule). Le listing 1 présente les modifications apportées à l'élément <httpHandlers> du web.config qui affecte les appels de service Web. Le HttpHandler par défaut utilisé pour traiter les appels .asmx est retiré et remplacé par la classe ScriptHandlerFactory située dans l'assembly System.Web.Extensions.dll. System.Web.Extensions.dll contient toutes les fonctionnalités utilisées par ASP.NET AJAX.

Listing 1. Configuration des Handler pour les Web Service ASP.NET AJAX
Sélectionnez

<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>

Ce changement de HttpHandler est effectué afin de permettre aux appels JSON (JavaScript Object Notation) d'être fait à partir de pages ASP.NET AJAX en direction de Web Services .NET, en utilisant un proxy JavaScript de service Web. ASP.NET AJAX envoie des messages JSON à des services Web, par opposition aux classiques appels SOAP (Simple Object Access Protocol) généralement associés à des services Web. Il en résulte des plus petits messages envoyés et reçus. Cela permet également des traitements plus efficaces des données côté client, depuis que la bibliothèque JavaScript d'ASP.NET AJAX est optimisée pour fonctionner avec des objets JSON. Les listing 2 et 3 montrent des exemples de requête à des service Web et les messages de réponse qui ont été sérialisés au format JSON. La requête montrée dans le listing 2 fait passer un pays en paramètre avec une valeur égale à "Belgique" alors que le message de réponse du listing 3 fait passer un tableau d'objets Customer et leurs propriétés associées.

Listing 2. Requête d'appel de Web Service sérialisée au format JSON
Sélectionnez

{"country":"Belgium"}

Note: the operation name is defined as part of the URL to the web service; additionally, request messages are not always submitted via JSON. Note: l'opération le nom est défini dans le cadre de l'URL du service web, en outre, la demande ne sont pas toujours les messages envoyés via JSON. Web Services can utilize the ScriptMethod attribute with the UseHttpGet parameter set to true, which causes parameters to be passed via a the query string parameters. Services Web peuvent utiliser l'attribut ScriptMethod avec l'UseHttpGet paramètre à true, ce qui provoque des paramètres à passer par l'intermédiaire d'une chaîne de paramètres de la requête.

Listing 3. Réponse d'un Web Service sérialisée au format JSON
Sélectionnez

[{"__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"}]

Dans la section suivante, vous verrez comment créer des services Web capables de traiter les requêtes d'appel JSON et de répondre à la fois avec des types simples et complexes.

Création d'un Web Service disponible pour l'AJAX (AJAX-Enabled Web Services)

Le framework ASP.NET AJAX fournit plusieurs façons différentes d'appeler des services Web. Vous pouvez utiliser le contrôle AutoCompleteExtender (disponible dans le Toolkit ASP.NET AJAX) ou du JavaScript. Toutefois, avant d'appeler un service, vous devez activer l'AJAX sur celui-ci de sorte qu'il puisse être appelé par le code client.

Peu importe que vous soyez débutant sur les Web Services d'ASP.NET, vous allez trouver ça simple de créer des services et de leur activer l'AJAX. Le framework. NET supporte la création de services Web ASP.NET depuis sa parution initiale en 2002 et les extensions ASP.NET AJAX fournissent des fonctionnalités AJAX supplémentaires qui s'appuient sur l'ensemble de fonctionnalités par défaut du framework. NET. Visual Studio. NET 2008 possède un support intégré de création de service Web .asmx et leurs fichiers de code associés qui dérivent de la classe System.Web.Services.WebService. A mesure que vous ajoutez des méthodes dans la classe, vous devez ajouter l'attribut WebMethod afin de leur permettre d'être appelée par les consommateurs de services Web.

Le listing 4 montre un exemple d'utilisation de l'attribut WebMethod sur une méthode nommée GetCustomersByCountry().

Listing 4. Utiliser l'attribut WebMethod dans un web service
Sélectionnez

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

La méthode GetCustomersByCountry() accepte un pays en paramètre et retourne un tableau d'objets Customer. La valeur du pays passée à la méthode est transmise à une couche métier qui, à son tour, appelle une couche de données pour récupérer les données de la base de données, remplir les propriétés de l'object Customer avec les données et retourner le tableau.

Utilisation de l'attribut ScriptService

Bien que l'ajout de l'attribut WebMethod permette à la méthode GetCustomersByCountry() d'être appelée par des clients qui envoient des messages SOAP au Web Service, il ne permet pas d'appels JSON à partir d'applications ASP.NET AJAX. Afin de permettre de faire des appels JSON, vous devez ajouter l'attribut ScriptService des extensions ASP.NET AJAX à la classe du service Web. Cela permet à un service Web d'envoyer les messages de réponse formatés en utilisant JSON et permet aux scripts côté client d'appeler un service en envoyant des messages JSON.

Le listing 5 montre un exemple de l'utilisation de l'attribut ScriptService sur un service Web nommé CustomersService.

Listing 5. Utilisation de l'attribut ScriptService pour activer l'AJAX sur un web service
Sélectionnez

[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);
	}
}

L'attribut ScriptService agit comme un marqueur qui indique qu'il peut être appelé à partir de script AJAX. Il ne traite pas actuellement toutes les tâches de sérialisation ou désérialisation JSON qui se produisent dans les coulisses. Le ScriptHandlerFactory (configuré dans le web.config) et d'autres classes s'occupent du gros de la transformation JSON.

Utilisation de l'attribut ScriptMethod

L'attribut ScriptService est le seul attribut d'ASP.NET AJAX qui doit être défini dans un Web Service .NET afin d'être utilisable par les pages ASP.NET AJAX. Toutefois, un autre attribut appelé ScriptMethod peut également être appliquée directement sur les méthodes Web dans un service. ScriptMethod définit trois propriétés dont UseHttpGet, ResponseFormat et XmlSerializeString. Changer les valeurs de ces propriétés peut être utile dans les cas où le type de la demande d'une méthode Web doit être passé à GET ou lorsqu'une méthode Web a besoin de retourner des données XML brutes sous la forme d'un objet XmlDocument ou d'un objet XmlElement ou alors lorsque les données retournées depuis un service doivent toujours être sérialisés en XML au lieu de JSON.

La propriété UseHttpGet peut être utilisée lorsque la méthode Web doit accepter les requêtes GET plutôt que les requêtes POST. Les demandes sont envoyées en utilisant une URL avec des paramètres d'entrée de méthode web convertis en paramètres de type QueryString. La propriété UseHttpGet est à faux (false) par défaut et ne devrait être mise à vrai (true) que lorsque les opérations sont connues pour être sécurisées et que des données sensibles ne sont pas transmises à un service Web. Le listing 6 montre un exemple d'utilisation de l'attribut ScriptMethod avec la propriété UseHttpGet.

Listing 6. Utilisation de l'attribut ScriptMethod avec la propriété UseHttpGet.
Sélectionnez

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

Un exemple de l'en-tête envoyée lors de l'appel à la méthode Web HttpGetEcho du listing 6 est affiché ci-dessous :

 
Sélectionnez

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

En plus de permettre aux méthodes web d'accepter les demandes HTTP GET, l'attribut ScriptMethod peut aussi être utilisé lorsque des réponses XML doivent être renvoyées plutôt que JSON. Par exemple, un service web peut récupérer un flux RSS d'un site distant et le retourner comme un objet XmlDocument ou XmlElement. Le traitement des données XML peut alors se produire sur le client.

Le listing 7 montre un exemple d'utilisation de la propriété ResponseFormat afin de spécifier que des données XML doivent être retournées depuis la méthode Web.

Listing 7. Utilisation de l'attribut ScriptMethod avnec la propriété ResponseFormat.
Sélectionnez

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

La propriété ResponseFormat peut également être utilisée avec la propriété XmlSerializeString. La propriété XmlSerializeString a une valeur par défaut à false (faux) qui signifie que tous les types de retour, sauf les chaînes, retournés depuis une méthode Web sont sérialisés en XML et lorsque la propriété ResponseFormat est définie à ResponseFormat.Xml. Lorsque XmlSerializeString est mis à true (vrai), tous les types de retour d'une méthode Web sont sérialisés en XML, y compris les chaînes. Si la propriété ResponseFormat a une valeur de ResponseFormat.Json, alors la propriété XmlSerializeString est ignorée.

Le listing 8 montre un exemple d'utilisation de la propriété XmlSerializeString afin de forcer les chaînes à être sérialisées en XML.

Listing 8. Utilisation de l'attribut ScriptMethod avec la propriété XmlSerializeString
Sélectionnez

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

La valeur retournée par l'appel de la méthode web GetXmlString que l'on peut voir dans le listing 8 est la suivante :

 
Sélectionnez
			
<?xml version="1.0"?>
<string>Test</string>

Bien que le format JSON par défaut réduise la taille globale de la demande et des messages de réponse, et rende la consommation plus facile par les clients ASP.NET AJAX dans n'importe quel navigateur, les propriétés ResponseFormat et XmlSerializeString peuvent être utilisées lorsque les applications clientes telles qu'Internet Explorer 5 ou supérieur attendent des données XML en retour d'une méthode web.

Travailler avec des types complexes

Le listing 5 montre un exemple de retour d'un type complexe (nommé Customer) à partir d'un service Web. La classe Customer définit plusieurs types simples en tant que propriétés, telles que FirstName et LastName. Les types complexes utilisés comme un paramètre d'entrée ou de retour d'une méthode web sont automatiquement sérialisés en JSON, avant d'être envoyés coté client. Toutefois, les types complexes imbriqués (Nested types : qui sont définis en interne, au sein d'un autre type) ne sont pas mis à la disposition du client comme des objets indépendants par défaut.

Dans le cas où un type complexe imbriqué utilisé par un service Web doit également être utilisé dans une page cliente, l'attribut GenerateScriptType d'ASP.NET AJAX peut être ajouté au Web Service. Par exemple, la classe CustomerDetails du listing 9 contient les propriétés Address et Gender qui représentent des types complexes imbriqués.

Listing 9. La classe CustomerDetails montrée ici contient deux types complexes imbriqués.
Sélectionnez

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; }
	}
}

Les objets Address et Gender définis à l'intérieur de la classe CustomerDetails du listing 9 ne seront pas automatiquement mis à disposition pour les utiliser côté client via JavaScript, car ce sont des types imbriqués (Address est une classe et Gender est une énumération). Dans les situations où un nested type, utilisé dans un service Web, doit être disponible côté client, l'attribut GenerateScriptType mentionnés précédemment peut être utilisé (voir listing 10). Cet attribut peut être ajouté à plusieurs reprises dans les cas où différents types complexes imbriqués sont renvoyés par un service. Il peut être appliqué directement sur la classe du service web ou sur les méthodes Web.

Listing 10.Utiliser l'attribut GenerateScriptService afin de définir des types imbriqués qui doivent être disponible pour le client.
Sélectionnez

[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 
}

En appliquant l'attribut GenerateScriptType au service Web, les types Address et Gender seront automatiquement mis à disposition pour utilisation par le code javascript ASP.NET AJAX coté client. Un exemple de JavaScript, qui est automatiquement généré et envoyé au client par l'ajout de l'attribut GenerateScriptType sur un service Web, est montré dans le listing 11. Vous verrez comment utiliser les types complexes imbriqués plus tard dans l'article.

Listing 11. Des types complexes imbriqués rendus disponible à une page ASP.NET AJAX.
Sélectionnez

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);

Maintenant que vous avez vu comment créer des services Web et comment les rendre accessibles aux pages ASP.NET AJAX, nous allons jeter un coup d'oeil à la façon de créer et d'utiliser des proxys JavaScript afin que les données puissent être récupérées ou envoyées à des services Web.

Créer des proxys JavaScript

Appeler un service Web standard (.NET ou d'une autre plate-forme) se traduit généralement par la création d'un objet proxy qui vous masque la complexité de l'envoi et la réception de requête SOAP. Avec les appels aux service Web ASP.NET AJAX, les proxys JavaScript peuvent être créés et utilisés pour appeler facilement des services sans se soucier de la sérialisation et de la désérialisation des messages JSON. Les proxys JavaScript peuvent être générés automatiquement en utilisant le contrôle ScriptManager d'ASP.NET AJAX.

La création d'un proxy JavaScript pouvant appeler des services Web se fait en utilisant la propriété Services du ScriptManager. Cette propriété vous permet de définir un ou plusieurs services qu'une page ASP.NET AJAX peut appeler de manière asynchrone pour envoyer ou recevoir des données sans nécessiter des opérations de postback. Vous définissez un service en utilisant le contrôle ServiceReference d'ASP.NET AJAX et en affectant l'URL du web service à la propriété Path. Le listing 12 montre un exemple de référencement d'un service nommé CustomersService.asmx.

Listing 12. Defining a Web Service used in an ASP.NET AJAX page.
Sélectionnez

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

Ajouter une référence à CustomersService.asmx par le biais du contrôle ScriptManager provoque la génération dynamique d'un proxy JavaScript et son référencement par la page. Le proxy est ajouté à la page grâce à la balise <script> et chargé dynamiquement en appelant le fichier CustomersService.asmx auquel aura été ajouté /js à la fin. L'exemple suivant montre comment le proxy JavaScript est intégré dans la page lorsque le débogage est désactivé dans le web.config :

 
Sélectionnez

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

Note: Si vous voulez voir le code JavaScript du proxy qui est généré, vous pouvez taper l'URL du service Web .Net voulu dans le champ d'adresse d'Internet Explorer et ajouter "/js" à la fin de celui-ci.

Si le débogage est activé dans le web.config, une version de débogage du proxy JavaScript sera incluse dans la page comme indiqué :

 
Sélectionnez

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

Le proxy JavaScript créé par le ScriptManager peut également être intégré directement dans la page plutôt que d'être référencé en utilisant l'attribut src de la balise <script>. Cela peut être fait en renseignant la propriété InlineScript du contrôle ServiceReference à true (la valeur par défaut est false). Cela peut être utile quand un proxy n'est pas partagé entre plusieurs pages et si vous souhaitez réduire le nombre d'appels fait au serveur. Lorsque InlineScript est à vrai, le script du proxy ne sera pas mis en cache par le navigateur, ainsi la valeur par défaut est recommandée dans le cas où le proxy est utilisé par plusieurs pages dans une application ASP.NET AJAX. Un exemple d'utilisation de la propriété InlineScript est affiché ci-dessous :

 
Sélectionnez

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

Utilisation des proxys JavaScript

Une fois que le service Web est référencé par une page ASP.NET AJAX en utilisant le contrôle ScriptManager, un appel peut être fait au service Web et les données renvoyées peuvent être manipulées en utilisant des fonctions de rappel (callback). Un service Web est appelé en faisant référence à son namespace (s'il en existe un), le nom de la classe et le nom de la méthode web. Les paramètres passés au service Web peuvent être définis avec une callback qui gère les données renvoyées.

Un exemple d'utilisation d'un proxy JavaScript pour appeler une méthode web, nommée GetCustomersByCountry(), est montré dans le listing 13. La fonction GetCustomersByCountry() est appelée quand un utilisateur clique sur un bouton de la page.

Listing 13. Appel d'un service Web avec un proxy javascript.
Sélectionnez

function GetCustomerByCountry()
{
	var country = $get("txtCountry").value;
	InterfaceTraining.CustomersService.GetCustomersByCountry(country, OnWSRequestComplete);
}

function OnWSRequestComplete(results)
{
	if (results != null)
	{
		CreateCustomersTable(results);
		GetMap(results);
	}
}

Cet appel fait référence à l'espace de nom InterfaceTraining, la classe CustomersService et la méthode web GetCustomersByCountry définis dans le service. Il passe en paramètre la valeur d'un pays obtenue à partir d'une zone de texte ainsi qu'une fonction de rappel nommée OnWSRequestComplete qui sera invoquée lorsque l'appel asynchrone du service Web se termine. OnWSRequestComplete gère le tableau d'objets Customer retourné par le service et les convertit en un tableau HTML qui est affiché dans la page. Le résultat généré par l'appel est montré figure 1.

Image non disponible
Figure 1: Liaison des données récupérées par l'appel AJAX asynchrone au Web Service.



Les proxys JavaScript peuvent aussi faire un simple appel de Web Services dans les cas où une méthode Web doit être appelée, mais que le proxy ne doit pas attendre de réponse. Par exemple, vous pouvez souhaiter appeler un service Web pour démarrer un processus comme un flux de travail, mais ne pas vouloir attendre une valeur de retour de la part du service. Dans ces cas où un appel à sens unique doit être fait à un service, la fonction de rappel du listing 13 peut être tout simplement omise. Comme aucune fonction callback est définie, l'objet proxy n'attendra pas les données de retour du service Web.

Gestion des erreurs

Les méthodes de rappels asynchrones des services Web peuvent rencontrer différents types d'erreurs telles qu'une coupure de réseau, un service Web inaccessible ou une exception retournée par le web service. Heureusement, les objets JavaScript du proxy générés par le ScriptManager permettent de définir de multiples fonctions de rappels pour gérer les erreurs et les échecs, en plus de la méthode de rappel en cas de réussite montrée plus tôt. Une callback d'erreur peut être définie immédiatement après la classique fonction de rappel lors de l'appel à la méthode Web comme le montre le listing 14.

Listing 14. Définition d'une méthode de rappel pour les erreurs et affichage de celles-ci.
Sélectionnez

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());
}

Toutes les erreurs qui se produisent lorsque le service Web est appelé déclencheront la méthode de rappel OnWSRequestFailed() qui accepte un objet représentant l'erreur en paramètre. L'object d'erreur expose plusieurs fonctions différentes pour déterminer la cause de l'erreur et savoir si l'appel a expiré ou non. Le listing 14 montre un exemple d'utilisation des différentes fonctions d'erreur et la figure 2 montre un exemple du contenu généré par ces fonctions.

Image non disponible
Figure 2: Résultat des méthodes d'erreur ASP.NET AJAX

Manipulation de données XML retournées depuis un Web Service

Plus haut, vous avez vu comment une méthode Web pouvait renvoyer des données XML brutes à l'aide de l'attribut ScriptMethod avec sa propriété ResponseFormat. Lorsque ResponseFormat est positionné à ResponseFormat.Xml, les données retournées par le service Web sont sérialisées en XML plutôt qu'en JSON. Cela peut être utile lorsque les données XML doivent être passées directement au client pour les traiter en utilisant JavaScript ou XSLT. A l'heure actuelle, Internet Explorer 5 ou supérieur offre le meilleur modèle objet côté client pour l'analyse et le filtrage de données XML en raison de son support intégré de MSXML.

La récupération des données XML à partir d'un service Web n'est pas différente de la récupération d'autres types de données. Il faut commencer par invoquer le proxy JavaScript pour appeler la fonction appropriée et définir une fonction de rappel. Une fois l'appel terminé, vous pouvez alors traiter les données dans la fonction de rappel.

Le listing 15 montre un exemple d'appel d'une méthode Web nommée GetRssFeed() qui retourne un objet XmlElement. GetRssFeed() accepte un paramètre unique représentant l'URL du flux RSS à récupérer.

Listing 15. Travailler avec du XML renvoyé par un Web Service.
Sélectionnez

function GetRss()
{
	InterfaceTraining.DemoService.GetRssFeed("http://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+";
	}
}

Cet exemple passe une URL à un flux RSS et traite le XML retourné dans la fonction OnWSRequestComplete(). Cette méthode vérifie dans un premier temps si le navigateur est Internet Explorer afin de savoir si l'analyseur MSXML est disponible. Si c'est le cas, une déclaration XPath est utilisée pour localiser toutes les balises <item> dans le flux RSS. On itère ensuite sur chaque élément et les balises <title> et <link> associées sont trouvées et traitées pour afficher chaque données de l'élément. La figure 3 présente un exemple de sortie générée par un appel ASP.NET AJAX par l'intermédiaire d'un proxy JavaScript à la méthode web GetRssFeed().

Image non disponible
Figure 3: Sortie générée lors de l'appel du service Web qui renvoie des données RSS.

Manipulation des types complexes

Les types complexes acceptés ou renvoyés par un service web sont automatiquement exposés par l'intermédiaire d'un proxy JavaScript. Toutefois, les types complexes imbriqués ne sont pas directement accessibles côté client, sauf si l'attribut GenerateScriptType est appliqué au service tel que discuté plus tôt. Mais pourquoi voudriez-vous utiliser un type complexe imbriqué côté client?

Pour répondre à cette question, supposons qu'une page ASP.NET AJAX affiche des données de clients et permette aux utilisateurs de mettre à jour l'adresse du client. Si le service web précise que le type Address (un type complexe défini dans une classe CustomerDetails) peut être envoyé au client, alors le processus de mise à jour peut être divisé en différentes fonctions pour une meilleure réutilisation du code.

Le listing 16 montre un exemple de code côté client qui appelle un objet Address défini dans un espace de nom Model, le remplit avec des données mises à jour et les assigne à la propriété Address de l'objet CustomerDetails. L'objet CustomerDetails est alors passé au Web Service pour son traitement.

Listing 16. Utilisation de types complexes imbriqués
Sélectionnez

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")+ "!");
}

Création et utilisation des méthodes de Page (PageMethods)

Les Web Services fournissent un excellent moyen d'exposer des services réutilisables à une variété de clients, y compris les pages ASP.NET AJAX. Toutefois, il peut y avoir des cas où une page doit récupérer des données qui ne sont jamais utilisées ou partagées par d'autres pages. Dans ce cas, créer un fichier .asmx pour permettre à la page d'accéder aux données peut sembler excessif puisque le service n'est utilisé que par une seule page.

ASP.NET AJAX fournit un autre mécanisme pour faire des appels qui ressemblent à des Web service sans avoir à créer des fichiers .asmx. Cela se fait en utilisant une technique dénommée "Méthode de pages" (PageMethods). Les méthodes de pages sont des méthodes statiques intégrées directement dans la page ou dans un fichier code-beside qui utilisent l'attribut WebMethod. En utilisant l'attribut WebMethod elles peuvent être appelées en utilisant un objet JavaScript particulier, nommé PageMethods, qui est créé dynamiquement lors de l'exécution. L'objet PageMethods agit comme un proxy qui vous abstrait du processus de sérialisation/désérialisation JSON. Notez que pour utiliser l'objet PageMethods, vous devez définir la propriété EnablePageMethods du ScriptManager à true.

 
Sélectionnez

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

Le listing 17 montre un exemple où sont définies deux méthodes de pages dans la classe de code behind ASP.NET. Ces méthodes permettent de récupérer des données à partir d'une couche métier située dans le dossier App_Code du site.

Listing 17. Définition de méthodes de page.
Sélectionnez

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

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

Lorsque le ScriptManager détecte la présence de méthodes Web dans la page, il génère une référence dynamique à l'objet PageMethods dont on a parlé plus tôt. L'appel d'une méthode Web est accompli en faisant référence à la classe PageMethods, suivi par le nom de la méthode et, le cas échéant, des paramètres qui doivent être passés. Le listing 18 montre des exemples d'appel de ces deux méthodes de pages indiquées précédemment.

Listing 18. Appel des méthodes de page avec l'objet JavaScript PageMethods.
Sélectionnez

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);
}

L'utilisation de l'objet PageMethods est très similaire à l'utilisation d'un objet proxy JavaScript. Vous devez d'abord spécifier tous les paramètres qui doivent être passés à la méthode de page et ensuite vous devez définir la fonction qui doit être appelée lorsque l'appel asynchrone se termine. Une callback d'échec peut également être précisée (voir le listing 14 pour un exemple de gestion des échecs).

Le contrôle AutoCompleteExtender et le Toolkit ASP.NET AJAX

Le Toolkit ASP.NET AJAX (disponible à cette adresse) offre plusieurs contrôles qui peuvent être utilisés pour accéder à des services Web. Plus particulièrement, le toolkit contient un contrôle très utile, l'AutoCompleteExtender, qui peut être utilisé pour appeler des services Web et afficher les données dans des pages sans écrire de code JavaScript.

Le contrôle AutoCompleteExtender peut être utilisé pour étendre les fonctionnalités existantes d'un textbox et aider les utilisateurs à repérer plus facilement les données qu'ils cherchent. Lors de la saisie dans la zone de texte, le contrôle peut être utilisé pour interroger un service Web et peut afficher des résultats sous la zone de texte dynamiquement. La figure 4 montre un exemple d'utilisation de l'AutoCompleteExtender pour afficher l'identifiant client pour une demande de support. A mesure que l'utilisateur tape des caractères dans la zone de texte, des éléments seront affichés en dessous en relation avec la saisie. Les utilisateurs peuvent alors choisir l'identifiant du client voulu.

L'utilisation de l'AutoCompleteExtender au sein d'une page ASP.NET AJAX nécessite que l'assembly AjaxControlToolkit.dll soit ajoutée au répertoire bin du site web. Une fois que l'assembly du toolkit a été ajoutée, vous pouvez y faire référence dans le web.config afin que les contrôles qu'il contient soient à la disposition de toutes les pages de l'application. Cela peut être fait par l'ajout de la balise suivante dans la section <controls> du web.config :

 
Sélectionnez

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

Dans le cas où vous ne devez utiliser le contrôle que dans une page spécifique, vous pouvez le référencer en ajoutant la directive Reference en haut d'une page comme montré ci dessous, au lieu de modifier le web.config :

 
Sélectionnez

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>
Image non disponible
Figure 4: Utilisation du contrôle AutoCompleteExtender.



Une fois que le site Web a été configuré pour utiliser le Toolkit ASP.NET AJAX, un AutoCompleteExtender peut être ajouté dans la page de le même façon que vous auriez ajouté un contrôle serveur ASP.NET. Le listing 19 montre un exemple d'utilisation du contrôle pour appeler un service Web.

Listing 19. Utilisation du contrôle AutoCompleteExtender de l'ASP.NET AJAX Toolkit .
Sélectionnez

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

Le contrôle AutoCompleteExtender a plusieurs propriétés, y compris les propriétés ID et runat que l'on trouve sur les contrôles serveurs. En outre, il vous permet de définir combien de caractères un utilisateur final peut taper avant que le service Web soit appelé. La propriété MinimumPrefixLength montrée dans le listing liste 19 permet au service d'être appelé à chaque fois qu'un caractère est tapé dans la zone de texte. Vous devez être prudent en définissant cette valeur car chaque fois que l'utilisateur tape un caractère, le service Web sera appelé à la recherche de valeurs qui correspondent aux caractères dans la zone de texte. Le service Web à appeler ainsi que la méthode Web cible sont définis en utilisant respectivement les propriétés ServicePath et ServiceMethod. Enfin, la propriété TargetControlID identifie le textbox à relier au contrôle AutoCompleteExtender.

Le service Web appelé doit avoir l'attribut ScriptService positionné, comme discuté plus tôt, et la méthode Web cible doit avoir deux paramètres, nommés prefixText et count. Le paramètre prefixText représente les caractères tapés par l'utilisateur final et le paramètre count représente le nombre d'éléments à retourner (la valeur par défaut est 10). Le listing 20 montre un exemple d'appel de la méthode Web GetCustomerIDs par le contrôle AutoCompleteExtender indiqué plus haut dans le listing 19. La méthode Web appelle une méthode de la couche métier qui, à son tour, appelle une méthode de la couche de données qui gère le filtrage des données et la restitution des résultats correspondants. Le code de la méthode de la couche de données est présenté dans le listing 21.

Listing 20. Filtrage des données envoyées depuis le contrôle AutoCompleteExtender.
Sélectionnez

[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count)
{
	return Biz.BAL.GetCustomerIDs(prefixText, count);
}
Listing 21. Filtrage des résultats basé sur la saisie de l'utilisateur.
Sélectionnez

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;
}

Conclusion

ASP.NET AJAX offre un excellent support pour appeler des services Web sans avoir à écrire beaucoup de code JavaScript pour gérer les messages d'appel et de retour. Dans cet article, vous avez vu comment ajouter le support de l'AJAX à des Web Services .NET afin de leur permettre de traiter les messages JSON et comment définir des proxys JavaScript en utilisant le contrôle ScriptManager. Vous avez également vu comment les proxys JavaScript peuvent être utilisés pour appeler des services Web, gérer les types simples et complexes et faire face aux échecs. Enfin, vous avez vu comment les méthodes de page peuvent être utilisées pour simplifier le processus de création et d'appel de service Web et comment le contrôle AutoCompleteExtender peut fournir de l'aide aux utilisateurs finaux lors de leurs saisies. Bien que l'UpdatePanel disponible dans ASP.NET AJAX sera certainement le contrôle de choix pour de nombreux programmeurs AJAX en raison de sa simplicité, savoir comment appeler des services Web par le biais de proxys JavaScript peut être utile dans de nombreuses applications.