Scarica l'esempio allegato
Dopo una breve introduzione dell’insieme di problematiche che risolve tale tecnologia, vedremo come è stata implementata ed infine come è resa disponibile all’interno di ASP.NET 2.0.
Postback e server roundtrip
Ormai gli sviluppatori ASP.NET hanno imparato a convinverci, sanno che se devono gestire un evento lato server devono farlo.
Stiamo parlando ovviamente della Postback, e cioè il meccanismo grazie al quale è attualmente possibile gestire degli eventi server a partire da eventi client.
La postback non è altro che un submit della pagina verso il server. Alla ricezione della richiesta, il server eseguirà il codice necessario a gestire l’evento scatenato sul client, e quindi fornirà una risposta utilizzata dal browser per ridisegnare completamente la pagina da presentare all’utente.
Data la natura “stateless” del protocollo HTTP su cui si basano le applicazioni web, per poter mantenere lo stato dei controlli tra i percorsi di andata e ritorno dal server, ASP.NET sfrutta il meccanismo del ViewState, un particolare campo nascosto del form in cui viene serializzato lo stato dei controlli, che viene quindi fatto transitare dal client al server ad ogni postback.
Con la postback è stato possibile modificare molto l’approccio alla programmazione per il web, rendendola sempre più simile alla programmazione per Windows (event-driven).
Purtroppo però, in alcuni casi, tale meccanismo di andata e ritorno dal server può risultare controproducente, sia in termini di codice da gestire, sia in termini di velocità di esecuzione e tempi di attesa per l’utente finale. Il server dovrà ogni volta rielaborare l’intera pagina e rispedire al client tutto il codice HTML.
Client Callback: come funziona…
E’ chiaro quindi che sarebbe molto più comodo e veloce, una volta caricata per la prima volta la pagina sul browser, poter richiamare direttamente dal client una routine sul server, riceverne una risposta, e agire di conseguenza sull’interfaccia client (la pagina web renderizzata) tramite DHTML per modificarne l’aspetto in base ai valori ricevuti dal server.
In questo caso infatti non sarebbe necessaria una postback (e quindi nemmeno il refresh completo della pagina) e lo stato dei controlli HTML verrebbe mantenuto direttamente sul client.
Tutto ciò è reso possibile grazie all’oggetto XMLHttpRequest.
Questo oggetto fu inizialmente implementato da Microsoft in Internet Explorer 5, sotto forma di controllo ActiveX. Successivamente ne fu sviluppata una versione compatibile incorporata come oggetto built-in dentro Mozilla 1.0 (su cui si basa FireFox), Netscape 7, e Safari 1.2.
Possiamo quindi tranquillamente affermare che c’è un buon supporto di questa tecnologia da parte dei browser più recenti.
XMLHttpRequest consente di inviare una richiesta verso una URL, e riceverne la risposta.
Per utilizzare questo oggetto è necessario crearne una istanza in base al tipo di browser, come riportato nel seguento esempio:
var req;
var url = “http://www.mysite.com/mypage.asp”;
// XMLHttpRequest native object (Mozilla)
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send(null);
}
// ActiveX version (IE/Windows)
else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req) {
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send();
}
}
…
function processReqChange() {
// controlla che lo stato sia “completed”
if (req.readyState == 4) {
// solo se la risposta è "OK"
if (req.status == 200) {
alert(req.responseText);
} else {
alert("Si sono verificati dei problemi lato server:\n" + req.statusText);
}
}
}
Come potete vedere, il pezzo di codice non fa altro che effettuare una chiamata verso l’URL specificato. La risposta (responseText) sarà gestita dentro l’handler dell’evento “req.onreadystatechange”, che verrà scatenato ogni volta che l’oggetto XMLHttpRequest cambierà stato (gestendo il caso “readyState == 4”, siamo sicuri di eseguire il codice solo quando l’oggetto avrà modificato il suo stato in “completed”, e quindi avrà terminato di caricare la risposta dal server).
La chiamata può avvenire in modo asincrono o sincrono (per modificare tale comportamento basta passare rispettivamente true o false come terzo parametro del metodo “XMLHttpRequest.open”). Nel caso di una richiesta sincrona, l’esecuzione dello script viene stoppata fino a quando la richiesta verso il server non viene portata a termine. Nel caso della richiesta asincrona, invece, l’esecuzione del codice continua, e l’utente può compiere delle altre azioni in attesa che la richiesta venga completata (e quindi gestita tramite l’evento “req.onreadystatechange”).
Client Callback: come è stata implementata in ASP.NET 2.0 …
Tutto il meccanismo illustrato nei paragrafi precedenti è brillantemente supportato in ASP.NET 2.0, che ne semplifica l’utilizzo e l’implementazione. Non dovremo infatti implementare la logica per gestire la chiamata con XMLHttpRequest, in quanto è già tutto implementato e reso ready-to-use tramite le API di ASP.NET.
Per poter utilizzare la callback in una web form è necessario che questa implementi l’interfaccia “ICallbackEventHandler”, utilizzando la direttiva “@Implements”:
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
Questo predispone la web form alla ricezione di chiamate di tipo callback. Implementando tale interfaccia è necessario infatti implementarne il metodo “RaiseCallbackEvent”, che sarà richiamato per gestire la richiesta e ritornare una risposta allo script lato client.
Come abbiamo detto, la callback ha origine sul client, in genere a seguito di un’azione dell’utente (per es. un click) che scatena l’esecuzione di codice javascript che si occuperà di fare la chiamata XMLHttpRequest.
ASP.NET 2.0 mette a disposizione, tramite le API del ClientScriptManager, il metodo statico “ClientScript.GetCallbackEventReference”. Questo metodo è molto importante, perché ha la funzione di:
- creare la stringa contenente il riferimento alla funzione javascript che si occuperà di avviare la callback (“WebForm_DoCallback”, che esamineremo di seguito)
- indicare ad ASP.NET che è necessario referenziare, nell’output HTML inviato al client, il file JavaScript in cui si trova la suddetta funzione (insieme ad altro codice javascript che gestisce la callback sul client)
Detto questo, andiamo a chiarire il funzionamento di tutto ciò, aiutandoci con un esempio concreto di codice.
In questo esempio (di cui potete scaricarne il codice completo) si vuole semplicemente richiedere l’orario corrente (o la data) del server a partire dal client, senza far ricaricare la pagina.
Innanzitutto è necessario implementare l’interfaccia “ICallbackEventHandler”:
<%@ Page Language="C#" %>
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
Andiamo quindi a definire il blocco di codice client, che include le due funzioni JavaScript. La prima verrà eseguite quando la callback tornerà sul client, mentre la seconda verrà eseguita nel caso in cui si verificasse un errore (facoltativa).
<script language="javascript">
function MyCallBackHandler(result, context)
{
…
alert("Server " + context + ": " + result);
…
}
function MyErrorHandler(result, context)
{
alert("An error has occured while retrieving data from the server.");
}
</script>
Per quanto riguarda il codice server, dobbiamo soltanto chiamare “GetCallbackEventReference” per ottenere la stringa con le reference alla funzione “WebForm_DoCallback” lato client, impostarla nell’attributo onclick del nostro button (aggiungendo il prefisso “javascript :”), e quindi implementare il metodo “RaiseCallbackEvent” che verrà eseguito lato server per generare la risposta da tornare al client.
<script runat="server">
private void Page_Load(Object sender, EventArgs e)
{
if (!IsCallback)
{
string callbackRef = ClientScript.GetCallbackEventReference(
this, // la web form
"document.all['TasksList'].value", // parametro da passare al server
"MyCallBackHandler", // funzione JavaScript che verrà richiamata quando la callback ritorna sul client
"document.all['TasksList'].value", // parametro di contesto da passare alla funzione lato client
"MyErrorHandler", // funzione JavaScript che verrà richiamata sul client in caso di errore
false); // async
…
BtnAutomatic.Attributes["onclick"] = String.Format("javascript : {0}", callbackRef);
…
}
}
public virtual string RaiseCallbackEvent (string eventArgument)
{
…
switch (eventArgument)
{
case "Time":
return DateTime.Now.TimeOfDay.ToString();
case "Date":
return DateTime.Now.Date.ToString();
default:
return "";
}
}
</script>
Infine includiamo nel corpo dell’html i controlli client che avvieranno la callback. Infatti, il valore della dropdownlist sarà passato come argomento alla funzione di callback per scegliere se ottenere l’ora o la data del server, e il button scatenerà la callback con il suo evento click.
<html>
<body>
Task:
<asp:dropdownlist id="TasksList" runat="server">
<asp:ListItem Text="get the time" Value="Time" Selected="True" />
<asp:ListItem Text="get the date" Value="Date" />
</asp:dropdownlist>
…
<input type="button" id="BtnAutomatic" runat="server" value="ASP.NET generated JS" />
…
</body>
</html>
Andando a guardare il sorgente HTML dell’output inviato al client, vedremo che innanzitutto è stato incluso il riferimento al javascript “di sistema” contenente, tra le altre, la funzione “WebForm_DoCallback”.
<script src="/ASPnet2_beta2/WebResource.axd?d=CPdjgdQ1YeebBNkes0Px8w2&t=632525444788495222" type="text/javascript"></script>
E poi vedremo che è stato aggiunto l’attributo onclick al nostro button, contenente la chiamata alla funzione “WebForm_DoCallback”.
<input type="button" id="BtnAutomatic" … onclick="javascript : WebForm_DoCallback('__Page',document.all['TasksList'].value,MyCallBackHandler,document.all['TasksList'].value,MyErrorHandler,false)" />
Esaminiamo quindi il flusso di chiamate che avvengono durante la callback, cercando di tenere traccia degli argomenti e dei valori di ritorno.
1) client side: al button click viene avviato il processo richiedendo la callback
WebForm_DoCallback(
'__Page',
document.all['TasksList'].value,
MyCallBackHandler,
document.all['TasksList'].value,
MyErrorHandler,
false)
2) server side: viene ricevuta e gestita la richiesta tramite “RaiseCallbackEvent”, che ritorna la stringa di risposta, che sarà automaticamente passata alla routine “MyCallBackHandler” lato client
public virtual string RaiseCallbackEvent (string eventArgument)
{
…
switch (eventArgument)
{
case "Time":
return DateTime.Now.TimeOfDay.ToString();
case "Date":
return DateTime.Now.Date.ToString();
default:
return "";
}
}
3) client side: l’oggetto XMLHttpRequest, internamento utilizzato da “WebForm_DoCallback”, riceve la risposta dal server, e quindi esegue la funzione JavaScript “MyCallBackHandler” passandole il valore di ritorno restituito come risposta dal server:
function MyCallBackHandler(result, context)
{
…
alert("Server " + context + ": " + result);
…
}
E’ possibile anche chiamare “manualmente” la funzione “WebForm_DoCallback”, magari ponendola all’interno di altre funzioni JavaScript che eseguono delle altre operazioni prima di avviare la callback.
Per fare ciò bisogna però ricordarsi di fare almeno una volta (anche se a vuoto) una chiamata al metodo “GetCallbackEventReference” sul server, senza la quale ASP.NET non includerebbe il JS con all’interno la funzione “WebForm_DoCallback”, generando quindi un errore sul client.
Ecco infine il risultato dell’esecuzione di questo esempio:

posted on lunedì 6 giugno 2005 1.14
by
Stefano Giannone