Informazioni sui servizi Web di ASP.NET AJAX
di Scott Cate
I servizi Web sono parte integrante di .NET Framework che offrono una soluzione multipiattaforma per lo scambio di dati tra sistemi distribuiti. Anche se i servizi Web vengono in genere usati per consentire a sistemi operativi diversi, modelli a oggetti e linguaggi di programmazione di inviare e ricevere dati, possono essere usati anche per inserire in modo dinamico i dati in una pagina ASP.NET AJAX o inviare dati da una pagina a un sistema back-end. Tutto questo può essere eseguito senza ricorrere alle operazioni di postback.
Chiamata di servizi Web con ASP.NET AJAX
Dan Wahlin
I servizi Web sono parte integrante di .NET Framework che offrono una soluzione multipiattaforma per lo scambio di dati tra sistemi distribuiti. Anche se i servizi Web vengono in genere usati per consentire a sistemi operativi diversi, modelli a oggetti e linguaggi di programmazione di inviare e ricevere dati, possono essere usati anche per inserire in modo dinamico i dati in una pagina ASP.NET AJAX o inviare dati da una pagina a un sistema back-end. Tutto questo può essere eseguito senza ricorrere alle operazioni di postback.
Anche se il controllo UpdatePanel AJAX ASP.NET offre un modo semplice per abilitare qualsiasi pagina ASP.NET, può verificarsi un momento in cui è necessario accedere in modo dinamico ai dati nel server senza usare UpdatePanel. In questo articolo verrà illustrato come eseguire questa operazione creando e utilizzando servizi Web all'interno di ASP.NET pagine AJAX.
Questo articolo è incentrato sulle funzionalità disponibili nel core ASP.NET estensioni AJAX, nonché su un controllo abilitato per il servizio Web nel toolkit ASP.NET AJAX denominato AutoCompleteExtender. Gli argomenti trattati includono la definizione di servizi Web abilitati per AJAX, la creazione di proxy client e la chiamata a Servizi Web con JavaScript. Si vedrà anche come è possibile effettuare chiamate al servizio Web direttamente ai metodi di ASP.NET pagina.
Configurazione dei servizi Web
Quando viene creato un nuovo progetto di sito Web con Visual Studio 2008, il file web.config include una serie di nuove aggiunte che potrebbero non essere note agli utenti delle versioni precedenti di Visual Studio. Alcune di queste modifiche eseguono il mapping del prefisso "asp" ai controlli AJAX ASP.NET in modo che possano essere usati nelle pagine, mentre altri definiscono i gestori Http obbligatori e HttpModules. L'elenco 1 mostra le modifiche apportate all'elemento <httpHandlers>
in web.config che influiscono sulle chiamate al servizio Web. L'oggetto HttpHandler predefinito usato per elaborare le chiamate asmx viene rimosso e sostituito con una classe ScriptHandlerFactory che si trova nell'assembly System.Web.Extensions.dll. System.Web.Extensions.dll contiene tutte le funzionalità di base usate da ASP.NET AJAX.
Elenco 1. configurazione del gestore del servizio Web AJAX ASP.NET
<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>
Questa sostituzione httpHandler viene eseguita per consentire l'effettuazione di chiamate JSON (JavaScript Object Notation) da ASP.NET pagine AJAX a servizi Web .NET tramite un proxy del servizio Web JavaScript. ASP.NET AJAX invia messaggi JSON a Servizi Web anziché alle chiamate SOAP (Simple Object Access Protocol) standard in genere associate ai servizi Web. In questo modo si ottengono messaggi di richiesta e risposta più piccoli nel complesso. Consente anche un'elaborazione lato client più efficiente dei dati, poiché la libreria JavaScript AJAX ASP.NET è ottimizzata per l'uso con oggetti JSON. L'elenco 2 e l'elenco 3 mostrano esempi di messaggi di richiesta e risposta del servizio Web serializzati in formato JSON. Il messaggio di richiesta visualizzato nell'elenco 2 passa un parametro country con il valore "Belgio" mentre il messaggio di risposta nell'elenco 3 passa una matrice di oggetti Customer e le relative proprietà associate.
Elenco 2. Messaggio di richiesta del servizio Web serializzato in JSON
{"country":"Belgium"}
> [! NOTA] il nome dell'operazione viene definito come parte dell'URL del servizio Web; inoltre, i messaggi di richiesta non vengono sempre inviati tramite JSON. I servizi Web possono utilizzare l'attributo ScriptMethod con il parametro UseHttpGet impostato su true, che fa sì che i parametri vengano passati tramite i parametri della stringa di query.
Elenco 3. Messaggio di risposta del servizio Web serializzato in JSON
[{"__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"}]
Nella sezione successiva verrà illustrato come creare servizi Web in grado di gestire i messaggi di richiesta JSON e rispondere con tipi semplici e complessi.
Creazione di servizi Web abilitati per AJAX
Il framework ASP.NET AJAX offre diversi modi per chiamare i servizi Web. È possibile usare il controllo AutoCompleteExtender (disponibile in ASP.NET AJAX Toolkit) o JavaScript. Tuttavia, prima di chiamare un servizio è necessario abilitarlo in modo che possa essere chiamato dal codice client-script.
Indipendentemente dal fatto che non si abbia familiarità con i servizi Web ASP.NET, è facile creare e abilitare i servizi AJAX. .NET Framework ha supportato la creazione di servizi Web ASP.NET sin dalla versione iniziale nel 2002 e le estensioni ASP.NET AJAX offrono funzionalità AJAX aggiuntive basate sul set predefinito di funzionalità di .NET Framework. Visual Studio .NET 2008 Beta 2 include il supporto predefinito per la creazione di file del servizio Web asmx e deriva automaticamente il codice associato accanto alle classi dalla classe System.Web.Services.WebService. Quando si aggiungono metodi alla classe, è necessario applicare l'attributo WebMethod affinché vengano chiamati dai consumer del servizio Web.
L'elenco 4 mostra un esempio di applicazione dell'attributo WebMethod a un metodo denominato GetCustomersByCountry().
Elenco 4. Uso dell'attributo WebMethod in un servizio Web
[WebMethod]
public Customer[] GetCustomersByCountry(string country)
{
return Biz.BAL.GetCustomersByCountry(country);
}
Il metodo GetCustomersByCountry() accetta un parametro country e restituisce una matrice di oggetti Customer. Il valore del paese passato al metodo viene inoltrato a una classe livello business che a sua volta chiama una classe livello dati per recuperare i dati dal database, riempire le proprietà dell'oggetto Customer con i dati e restituire la matrice.
Uso dell'attributo ScriptService
Durante l'aggiunta dell'attributo WebMethod, il metodo GetCustomersByCountry() viene chiamato dai client che inviano messaggi SOAP standard al servizio Web, non consente l'esecuzione di chiamate JSON da ASP.NET applicazioni AJAX predefinite. Per consentire l'applicazione delle chiamate JSON, è necessario applicare l'attributo dell'estensione ScriptService
AJAX ASP.NET alla classe servizio Web. Ciò consente a un servizio Web di inviare messaggi di risposta formattati tramite JSON e consente allo script lato client di chiamare un servizio inviando messaggi JSON.
L'elenco 5 mostra un esempio di applicazione dell'attributo ScriptService a una classe del servizio Web denominata CustomersService.
Elenco 5. Uso dell'attributo ScriptService per abilitare aJAX un servizio 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);
}
}
L'attributo ScriptService funge da marcatore che indica che può essere chiamato dal codice script AJAX. In realtà non gestisce alcuna attività di serializzazione o deserializzazione JSON eseguite in background. ScriptHandlerFactory (configurato in web.config) e altre classi correlate eseguono la maggior parte dell'elaborazione JSON.
Uso dell'attributo ScriptMethod
L'attributo ScriptService è l'unico attributo AJAX ASP.NET che deve essere definito in un servizio Web .NET affinché venga usato da ASP.NET pagine AJAX. Tuttavia, un altro attributo denominato ScriptMethod può essere applicato direttamente ai metodi Web in un servizio. ScriptMethod definisce tre proprietà, tra cui UseHttpGet
, ResponseFormat
e XmlSerializeString
. La modifica dei valori di queste proprietà può essere utile nei casi in cui il tipo di richiesta accettato da un metodo Web deve essere modificato in GET, quando un metodo Web deve restituire dati XML non elaborati sotto forma di oggetto XmlDocument
o XmlElement
o quando i dati restituiti da un servizio devono essere sempre serializzati come XML anziché JSON.
La proprietà UseHttpGet può essere utilizzata quando un metodo Web deve accettare richieste GET anziché richieste POST. Le richieste vengono inviate usando un URL con i parametri di input del metodo Web convertiti in parametri QueryString. Per impostazione predefinita, la proprietà UseHttpGet è false e deve essere impostata true
solo su quando le operazioni sono note per essere sicure e quando i dati sensibili non vengono passati a un servizio Web. L'elenco 6 mostra un esempio di utilizzo dell'attributo ScriptMethod con la proprietà UseHttpGet.
Elenco 6. Utilizzo dell'attributo ScriptMethod con la proprietà UseHttpGet.
[WebMethod]
[ScriptMethod(UseHttpGet = true)]
public string HttpGetEcho(string input)
{
return input;
}
Di seguito è riportato un esempio delle intestazioni inviate quando viene chiamato il metodo Web HttpGetEcho illustrato nell'elenco 6:
GET /CustomerViewer/DemoService.asmx/HttpGetEcho?input=%22Input Value%22 HTTP/1.1
Oltre a consentire ai metodi Web di accettare richieste HTTP GET, l'attributo ScriptMethod può essere usato anche quando le risposte XML devono essere restituite da un servizio anziché da JSON. Ad esempio, un servizio Web può recuperare un feed RSS da un sito remoto e restituirlo come oggetto XmlDocument o XmlElement. L'elaborazione dei dati XML può quindi verificarsi nel client.
L'elenco 7 mostra un esempio di utilizzo della proprietà ResponseFormat per specificare che i dati XML devono essere restituiti da un metodo Web.
Elenco 7. Utilizzo dell'attributo ScriptMethod con la proprietà ResponseFormat.
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlElement GetRssFeed(string url)
{
XmlDocument doc = new XmlDocument();
doc.Load(url);
return doc.DocumentElement;
}
La proprietà ResponseFormat può essere utilizzata anche insieme alla proprietà XmlSerializeString. La proprietà XmlSerializeString ha un valore predefinito false, il che significa che tutti i tipi restituiti tranne le stringhe restituite da un metodo Web vengono serializzati come XML quando la ResponseFormat
proprietà è impostata su ResponseFormat.Xml
. Quando XmlSerializeString
è impostato su true
, tutti i tipi restituiti da un metodo Web vengono serializzati come XML, inclusi i tipi stringa. Se la proprietà ResponseFormat ha un valore della ResponseFormat.Json
proprietà XmlSerializeString viene ignorata.
L'elenco 8 mostra un esempio di utilizzo della proprietà XmlSerializeString per forzare la serializzazione delle stringhe come XML.
Elenco 8. Utilizzo dell'attributo ScriptMethod con la proprietà XmlSerializeString
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml,XmlSerializeString=true)]
public string GetXmlString(string input)
{
return input;
}
Il valore restituito dalla chiamata al metodo Web GetXmlString illustrato nell'elenco 8 è illustrato di seguito:
<?xml version="1.0"?>
<string>Test</string>
Anche se il formato JSON predefinito riduce al minimo le dimensioni complessive dei messaggi di richiesta e risposta e viene utilizzato più facilmente dai client AJAX ASP.NET in modo incrociato, le proprietà ResponseFormat e XmlSerializeString possono essere utilizzate quando le applicazioni client come Internet Explorer 5 o versione successiva prevedono che i dati XML vengano restituiti da un metodo Web.
Utilizzo di tipi complessi
L'elenco 5 ha mostrato un esempio di restituzione di un tipo complesso denominato Customer da un servizio Web. La classe Customer definisce internamente diversi tipi semplici come proprietà, ad esempio FirstName e LastName. I tipi complessi usati come parametro di input o tipo restituito in un metodo Web abilitato per AJAX vengono serializzati automaticamente in JSON prima di essere inviati al lato client. Tuttavia, i tipi complessi annidati (quelli definiti internamente all'interno di un altro tipo) non vengono resi disponibili al client come oggetti autonomi per impostazione predefinita.
Nei casi in cui un tipo complesso annidato usato da un servizio Web deve essere usato anche in una pagina client, è possibile aggiungere l'attributo AJAX GenerateScriptType ASP.NET al servizio Web. Ad esempio, la classe CustomerDetails illustrata nell'elenco 9 contiene proprietà Address e Gender che rappresentano tipi complessi annidati.
Elenco 9. La classe CustomerDetails illustrata di seguito contiene due tipi complessi annidati.
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; }
}
}
Gli oggetti Address e Gender definiti all'interno della classe CustomerDetails mostrati nell'elenco 9 non verranno automaticamente resi disponibili per l'uso sul lato client tramite JavaScript perché sono tipi annidati (Address è una classe e Gender è un'enumerazione). Nei casi in cui un tipo annidato usato all'interno di un servizio Web deve essere disponibile sul lato client, è possibile usare l'attributo GenerateScriptType indicato in precedenza (vedere Listato 10). Questo attributo può essere aggiunto più volte nei casi in cui diversi tipi complessi annidati vengono restituiti da un servizio. Può essere applicato direttamente alla classe servizio Web o a metodi Web specifici.
Elenco 10. Uso dell'attributo GenerateScriptService per definire tipi annidati che devono essere disponibili per il client.
[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
}
Applicando l'attributo GenerateScriptType
al servizio Web, i tipi Address e Gender verranno automaticamente resi disponibili per l'uso da parte del client ASP.NET codice JavaScript AJAX. Un esempio di JavaScript generato e inviato automaticamente al client aggiungendo l'attributo GenerateScriptType in un servizio Web viene visualizzato nell'elenco 11. Si vedrà come usare i tipi complessi annidati più avanti nell'articolo.
Elenco 11. Tipi complessi annidati resi disponibili per una pagina 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);
Ora che si è visto come creare servizi Web e renderli accessibili per ASP.NET pagine AJAX, si esaminerà come creare e usare proxy JavaScript in modo che i dati possano essere recuperati o inviati ai servizi Web.
Creazione di proxy JavaScript
La chiamata a un servizio Web standard (.NET o un'altra piattaforma) comporta in genere la creazione di un oggetto proxy che protegge l'utente dalle complessità dell'invio di messaggi di richiesta e risposta SOAP. Con ASP.NET chiamate al servizio Web AJAX, è possibile creare e usare proxy JavaScript per chiamare facilmente i servizi senza doversi preoccupare della serializzazione e della deserializzazione dei messaggi JSON. I proxy JavaScript possono essere generati automaticamente usando il controllo ScriptManager AJAX ASP.NET.
La creazione di un proxy JavaScript in grado di chiamare i servizi Web viene eseguita usando la proprietà Services di ScriptManager. Questa proprietà consente di definire uno o più servizi che una pagina ASP.NET AJAX può chiamare in modo asincrono per inviare o ricevere dati senza richiedere operazioni di postback. Per definire un servizio, utilizzare il controllo AJAX ServiceReference
ASP.NET e assegnare l'URL del servizio Web alla proprietà del Path
controllo. L'elenco 12 mostra un esempio di riferimento a un servizio denominato CustomersService.asmx.
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/CustomersService.asmx" />
</Services>
</asp:ScriptManager>
Elenco 12. Definizione di un servizio Web usato in una pagina ASP.NET AJAX.
L'aggiunta di un riferimento a CustomersService.asmx tramite il controllo ScriptManager fa sì che un proxy JavaScript venga generato dinamicamente e a cui fa riferimento la pagina. Il proxy viene incorporato usando il <tag script> e caricato dinamicamente chiamando il file CustomersService.asmx e aggiungendo /js alla fine di esso. L'esempio seguente mostra come il proxy JavaScript viene incorporato nella pagina quando il debug è disabilitato in web.config:
<script src="CustomersService.asmx/js" type="text/javascript"></script>
> [! NOTA] Se si vuole visualizzare il codice proxy JavaScript effettivo generato, è possibile digitare l'URL del servizio Web .NET desiderato nella casella dell'indirizzo di Internet Explorer e aggiungere /js alla fine di esso.
Se il debug è abilitato in web.config, una versione di debug del proxy JavaScript verrà incorporata nella pagina, come illustrato di seguito:
<script src="CustomersService.asmx/jsdebug" type="text/javascript"></script>
Il proxy JavaScript creato da ScriptManager può anche essere incorporato direttamente nella pagina anziché fare riferimento usando l'attributo <src del tag di script> . Questa operazione può essere eseguita impostando la proprietà InlineScript del controllo ServiceReference su true (il valore predefinito è false). Ciò può essere utile quando un proxy non viene condiviso tra più pagine e quando si vuole ridurre il numero di chiamate di rete effettuate al server. Quando InlineScript è impostato su true, lo script proxy non verrà memorizzato nella cache dal browser, pertanto il valore predefinito false è consigliato nei casi in cui il proxy viene usato da più pagine in un'applicazione AJAX ASP.NET. Di seguito è riportato un esempio di utilizzo della proprietà InlineScript:
<asp:ServiceReference InlineScript="true" Path="~/CustomersService.asmx"/>
Uso dei proxy JavaScript
Dopo aver fatto riferimento a un servizio Web da una pagina ASP.NET AJAX tramite il controllo ScriptManager, è possibile effettuare una chiamata al servizio Web e i dati restituiti possono essere gestiti usando le funzioni di callback. Un servizio Web viene chiamato facendo riferimento allo spazio dei nomi (se presente), al nome della classe e al nome del metodo Web. Tutti i parametri passati al servizio Web possono essere definiti insieme a una funzione di callback che gestisce i dati restituiti.
Un esempio di uso di un proxy JavaScript per chiamare un metodo Web denominato GetCustomersByCountry() è illustrato nell'elenco 13. La funzione GetCustomersByCountry() viene chiamata quando un utente finale fa clic su un pulsante nella pagina.
Elenco 13. Chiamata di un servizio Web con un proxy JavaScript.
function GetCustomerByCountry()
{
var country = $get("txtCountry").value;
InterfaceTraining.CustomersService.GetCustomersByCountry(country, OnWSRequestComplete);
}
function OnWSRequestComplete(results)
{
if (results != null)
{
CreateCustomersTable(results);
GetMap(results);
}
}
Questa chiamata fa riferimento allo spazio dei nomi InterfaceTraining, alla classe CustomersService e al metodo Web GetCustomersByCountry definito nel servizio. Passa un valore country ottenuto da una casella di testo e da una funzione di callback denominata OnWSRequestComplete che deve essere richiamata quando viene restituita la chiamata asincrona al servizio Web. OnWSRequestComplete gestisce la matrice di oggetti Customer restituiti dal servizio e li converte in una tabella visualizzata nella pagina. L'output generato dalla chiamata è illustrato nella figura 1.
Figura 1: Associazione di dati ottenuti effettuando una chiamata asincrona AJAX a un servizio Web. (Fare clic per visualizzare l'immagine a dimensione intera)
I proxy JavaScript possono anche effettuare chiamate unidirezionale ai servizi Web nei casi in cui deve essere chiamato un metodo Web, ma il proxy non deve attendere una risposta. Ad esempio, è possibile chiamare un servizio Web per avviare un processo, ad esempio un flusso di lavoro, ma non attendere un valore restituito dal servizio. Nei casi in cui è necessario effettuare una chiamata unidirezionale a un servizio, la funzione di callback illustrata nell'elenco 13 può essere semplicemente omessa. Poiché non viene definita alcuna funzione di callback, l'oggetto proxy non attenderà che il servizio Web restituisca i dati.
Gestione degli errori
I callback asincroni ai servizi Web possono riscontrare diversi tipi di errori, ad esempio la rete inattiva, il servizio Web non è disponibile o viene restituita un'eccezione. Fortunatamente, gli oggetti proxy JavaScript generati da ScriptManager consentono di definire più callback per gestire errori ed errori oltre al callback riuscito illustrato in precedenza. Una funzione di callback di errore può essere definita immediatamente dopo la funzione di callback standard nella chiamata al metodo Web, come illustrato nell'elenco 14.
Elenco 14. Definizione di una funzione di callback di errore e visualizzazione degli errori.
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());
}
Eventuali errori che si verificano quando viene chiamato il servizio Web attiveranno la funzione di callback OnWSRequestFailed() che accetta un oggetto che rappresenta l'errore come parametro. L'oggetto errore espone diverse funzioni per determinare la causa dell'errore e se si è verificato o meno il timeout della chiamata. L'elenco 14 mostra un esempio di utilizzo delle diverse funzioni di errore e la figura 2 mostra un esempio dell'output generato dalle funzioni.
Figura 2: Output generato chiamando ASP.NET funzioni di errore AJAX. (Fare clic per visualizzare l'immagine a dimensione intera)
Gestione dei dati XML restituiti da un servizio Web
In precedenza è stato illustrato come un metodo Web potrebbe restituire dati XML non elaborati usando l'attributo ScriptMethod insieme alla relativa proprietà ResponseFormat. Quando ResponseFormat è impostato su ResponseFormat.Xml, i dati restituiti dal servizio Web vengono serializzati come XML anziché COME JSON. Ciò può essere utile quando i dati XML devono essere passati direttamente al client per l'elaborazione tramite JavaScript o XSLT. Al momento corrente, Internet Explorer 5 o versione successiva offre il miglior modello a oggetti lato client per l'analisi e il filtro dei dati XML a causa del supporto predefinito per MSXML.
Il recupero di dati XML da un servizio Web non è diverso dal recupero di altri tipi di dati. Per iniziare, richiamare il proxy JavaScript per chiamare la funzione appropriata e definire una funzione di callback. Una volta restituita la chiamata, è possibile elaborare i dati nella funzione di callback.
L'elenco 15 mostra un esempio di chiamata a un metodo Web denominato GetRssFeed() che restituisce un oggetto XmlElement. GetRssFeed() accetta un singolo parametro che rappresenta l'URL del feed RSS da recuperare.
Elenco 15. Utilizzo dei dati XML restituiti da un servizio Web.
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+";
}
}
Questo esempio passa un URL a un feed RSS ed elabora i dati XML restituiti nella funzione OnWSRequestComplete(). OnWSRequestComplete() verifica innanzitutto se il browser è Internet Explorer per sapere se il parser MSXML è disponibile o meno. In caso affermativo, viene usata un'istruzione XPath per individuare tutti i <tag di elemento> all'interno del feed RSS. Ogni elemento viene quindi iterato e i tag di titolo> e <collegamento> associati <vengono individuati ed elaborati per visualizzare i dati di ogni elemento. La figura 3 mostra un esempio dell'output generato dall'esecuzione di una chiamata AJAX ASP.NET tramite un proxy JavaScript al metodo Web GetRssFeed().
Gestione dei tipi complessi
I tipi complessi accettati o restituiti da un servizio Web vengono esposti automaticamente tramite un proxy JavaScript. Tuttavia, i tipi complessi annidati non sono direttamente accessibili sul lato client, a meno che l'attributo GenerateScriptType non venga applicato al servizio come illustrato in precedenza. Perché usare un tipo complesso annidato sul lato client?
Per rispondere a questa domanda, si supponga che una pagina ASP.NET AJAX visualizzi i dati dei clienti e consenta agli utenti finali di aggiornare l'indirizzo di un cliente. Se il servizio Web specifica che il tipo di indirizzo (un tipo complesso definito all'interno di una classe CustomerDetails) può essere inviato al client, il processo di aggiornamento può essere suddiviso in funzioni separate per un riutilizzo del codice migliore.
Figura 3: Creare output dalla chiamata a un servizio Web che restituisce dati RSS. (Fare clic per visualizzare l'immagine a dimensione intera)
L'elenco 16 mostra un esempio di codice lato client che richiama un oggetto Address definito in uno spazio dei nomi Model, lo riempie di dati aggiornati e lo assegna alla proprietà Address di un oggetto CustomerDetails. L'oggetto CustomerDetails viene quindi passato al servizio Web per l'elaborazione.
Elenco 16. Uso di tipi complessi annidati
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")+ "!");
}
Creazione e utilizzo di metodi di pagina
I servizi Web offrono un modo eccellente per esporre servizi riabilitabili a un'ampia gamma di client, tra cui ASP.NET pagine AJAX. Tuttavia, possono verificarsi casi in cui una pagina deve recuperare i dati che non verranno mai usati o condivisi da altre pagine. In questo caso, la creazione di un file con estensione asmx per consentire alla pagina di accedere ai dati può sembrare eccessiva poiché il servizio viene usato solo da una singola pagina.
ASP.NET AJAX fornisce un altro meccanismo per effettuare chiamate simili ai servizi Web senza creare file asmx autonomi. Questa operazione viene eseguita usando una tecnica denominata "metodi di pagina". I metodi page sono metodi statici (condivisi in VB.NET) incorporati direttamente in un file di pagina o di codice accanto a cui è applicato l'attributo WebMethod. Applicando l'attributo WebMethod, è possibile chiamare usando uno speciale oggetto JavaScript denominato PageMethods che viene creato dinamicamente in fase di esecuzione. L'oggetto PageMethods funge da proxy che protegge l'utente dal processo di serializzazione/deserializzazione JSON. Si noti che per utilizzare l'oggetto PageMethods è necessario impostare la proprietà EnablePageMethods di ScriptManager su true.
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
</asp:ScriptManager>
L'elenco 17 mostra un esempio di definizione di due metodi di pagina in una classe code-beside ASP.NET. Questi metodi recuperano i dati da una classe di livello aziendale che si trova nella cartella App_Code del sito Web.
Elenco 17. Definizione dei metodi di pagina.
[WebMethod]
public static Customer[] GetCustomersByCountry(string country)
{
return Biz.BAL.GetCustomersByCountry(country);
}
[WebMethod]
public static Customer[] GetCustomersByID(string id)
{
return Biz.BAL.GetCustomersByID(id);
}
Quando ScriptManager rileva la presenza di metodi Web nella pagina, genera un riferimento dinamico all'oggetto PageMethods indicato in precedenza. La chiamata a un metodo Web viene eseguita facendo riferimento alla classe PageMethods seguita dal nome del metodo e dai dati dei parametri necessari che devono essere passati. L'elenco 18 mostra esempi di chiamata dei due metodi di pagina mostrati in precedenza.
Elenco 18. Chiamata di metodi di pagina con l'oggetto JavaScript PageMethods.
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'uso dell'oggetto PageMethods è molto simile all'uso di un oggetto proxy JavaScript. È prima di tutto necessario specificare tutti i dati dei parametri che devono essere passati al metodo page e quindi definire la funzione di callback che deve essere chiamata quando viene restituita la chiamata asincrona. È anche possibile specificare un callback di errore (vedere Listato 14 per un esempio di gestione degli errori).
AutoCompleteExtender e ASP.NET AJAX Toolkit
Il ASP.NET AJAX Toolkit (disponibile da https://github.com/DevExpress/AjaxControlToolkit) offre diversi controlli che possono essere usati per accedere ai servizi Web. In particolare, il toolkit contiene un controllo utile denominato AutoCompleteExtender
che può essere usato per chiamare i servizi Web e mostrare i dati nelle pagine senza scrivere codice JavaScript.
Il controllo AutoCompleteExtender può essere usato per estendere le funzionalità esistenti di una casella di testo e aiutare gli utenti a individuare più facilmente i dati che stanno cercando. Quando digitano in una casella di testo, è possibile usare il controllo per eseguire query su un servizio Web e visualizzare i risultati sotto la casella di testo in modo dinamico. La figura 4 mostra un esempio di utilizzo del controllo AutoCompleteExtender per visualizzare gli ID cliente per un'applicazione di supporto. Man mano che l'utente digita caratteri diversi nella casella di testo, gli elementi diversi verranno visualizzati di seguito in base all'input. Gli utenti possono quindi selezionare l'ID cliente desiderato.
L'utilizzo di AutoCompleteExtender all'interno di una pagina AJAX ASP.NET richiede l'aggiunta dell'assembly AjaxControlToolkit.dll alla cartella bin del sito Web. Dopo aver aggiunto l'assembly del toolkit, è necessario farvi riferimento in web.config in modo che i controlli in esso contenuti siano disponibili per tutte le pagine di un'applicazione. A tale scopo, aggiungere il tag seguente all'interno del tag dei controlli> web.config<:
<add namespace="AjaxControlToolkit" assembly="AjaxControlToolkit" tagPrefix="ajaxToolkit"/>
Nei casi in cui è sufficiente usare il controllo in una pagina specifica, è possibile farvi riferimento aggiungendo la direttiva Reference all'inizio di una pagina come illustrato di seguito anziché aggiornare web.config:
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit"
TagPrefix="ajaxToolkit" %>
Figura 4: Uso del controllo AutoCompleteExtender. (Fare clic per visualizzare l'immagine a dimensione intera)
Dopo aver configurato il sito Web per l'uso di ASP.NET AJAX Toolkit, è possibile aggiungere un controllo AutoCompleteExtender nella pagina in modo analogo a quello di un normale controllo server ASP.NET. L'elenco 19 mostra un esempio di utilizzo del controllo per chiamare un servizio Web.
Elenco 19. Uso del controllo AJAX Toolkit AutoCompleteExtender ASP.NET.
<ajaxToolkit:AutoCompleteExtender ID="extTxtCustomerID" runat="server"
MinimumPrefixLength="1" ServiceMethod="GetCustomerIDs"
ServicePath="~/CustomersService.asmx"
TargetControlID="txtCustomerID" />
AutoCompleteExtender include diverse proprietà, tra cui l'ID standard e le proprietà runat presenti nei controlli server. Oltre a questi, consente di definire il numero di caratteri che un utente finale digita prima che venga eseguita una query sul servizio Web per i dati. La proprietà MinimumPrefixLength visualizzata nell'elenco 19 fa sì che il servizio venga chiamato ogni volta che un carattere viene digitato nella casella di testo. È necessario prestare attenzione impostando questo valore perché ogni volta che l'utente digita un carattere, verrà chiamato il servizio Web per cercare i valori che corrispondono ai caratteri nella casella di testo. Il servizio Web da chiamare e il metodo Web di destinazione vengono definiti rispettivamente usando le proprietà ServicePath e ServiceMethod. Infine, la proprietà TargetControlID identifica la casella di testo a cui associare il controllo AutoCompleteExtender.
Il servizio Web chiamato deve avere l'attributo ScriptService applicato come descritto in precedenza e il metodo Web di destinazione deve accettare due parametri denominati prefixText e count. Il parametro prefixText rappresenta i caratteri digitati dall'utente finale e il parametro count rappresenta il numero di elementi da restituire (il valore predefinito è 10). L'elenco 20 mostra un esempio del metodo Web GetCustomerIDs chiamato dal controllo AutoCompleteExtender illustrato in precedenza in Listato 19. Il metodo Web chiama un metodo livello business che a sua volta chiama un metodo livello dati che gestisce il filtro dei dati e la restituzione dei risultati corrispondenti. Il codice per il metodo del livello dati viene visualizzato nell'elenco 21.
Elenco 20. Filtro dei dati inviati dal controllo AutoCompleteExtender.
[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count)
{
return Biz.BAL.GetCustomerIDs(prefixText, count);
}
Elenco 21. Filtro dei risultati in base all'input dell'utente finale.
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;
}
Conclusione
ASP.NET AJAX offre un eccellente supporto per chiamare servizi Web senza scrivere un sacco di codice JavaScript personalizzato per gestire i messaggi di richiesta e risposta. In questo articolo è stato illustrato come abilitare I servizi Web .NET per abilitarli per l'elaborazione di messaggi JSON e come definire proxy JavaScript usando il controllo ScriptManager. Si è anche visto come usare i proxy JavaScript per chiamare i servizi Web, gestire tipi semplici e complessi e gestire gli errori. Infine, si è visto come usare i metodi di pagina per semplificare il processo di creazione e esecuzione di chiamate al servizio Web e come il controllo AutoCompleteExtender può fornire assistenza agli utenti finali durante la digitazione. Anche se UpdatePanel disponibile in ASP.NET AJAX sarà certamente il controllo di scelta per molti programmatori AJAX a causa della sua semplicità, sapere come chiamare i servizi Web tramite proxy JavaScript può essere utile in molte applicazioni.
Biografia
Dan Wahlin (Microsoft Most Valuable Professional per ASP.NET e servizi Web XML) è un insegnante di sviluppo .NET e consulente dell'architettura presso Interface Technical Training (http://www.interfacett.com). Dan ha fondato il codice XML per ASP.NET Developers Web site (www.XMLforASP.NET), si trova nel Bureau del relatore INETA e parla in diverse conferenze. 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 and authored XML for ASP.NET Developers (Sams). Quando non scrive codice, articoli o libri, Dan si diverte a scrivere e registrare musica e giocare a golf e basket con la moglie e i figli.
Scott Cate lavora con le tecnologie Web Microsoft dal 1997 ed è il presidente di myKB.com (www.myKB.com) dove si è specializzato nella scrittura di applicazioni basate su ASP.NET incentrate sulle soluzioni software della Knowledge Base. Scott può essere contattato via e-mail all'indirizzo o al scott.cate@myKB.com suo blog all'indirizzo ScottCate.com