Esempio di callback client con implementazione di convalida
Aggiornamento: novembre 2007
In un callback client una funzione script client invia una richiesta alla pagina Web ASP.NET, che esegue quindi una versione abbreviata del suo ciclo di vita normale per elaborare il callback. Per assicurarsi che gli eventi di callback abbiano origine dall'interfaccia utente prevista, è possibile convalidare i callback. Nella convalida di callback si registra un evento per la convalida durante il rendering della pagina Web e quindi si convalida l'evento durante il callback.
Nota: |
---|
La convalida dell'evento consente di proteggere l'applicazione Web da postback manomessi ma non da attacchi di tipo replay. Uno schema di convalida dell'evento più completo dovrebbe tenere conto della specificità dell'applicazione Web e delle autorizzazioni dell'utente che accede alle risorse. Per ulteriori informazioni, vedere Protezione delle applicazioni Web ASP.NET. |
L'esempio qui illustrato estende l'Esempio di implementazione di callback client (C#) e l'Esempio di implementazione di callback client (Visual Basic). In tali esempi un controllo ListBox denominato ListBox1 è un controllo lato server che visualizza un elenco di prodotti. Un elemento <button> HTML (non un controllo server Button) esegue un callback per ottenere informazioni di inventario del prodotto. L'esempio viene esteso per introdurre informazioni aggiuntive relative al fatto che un prodotto sia in vendita o meno e consentire la visualizzazione di tali informazioni solo da parte di utenti autenticati. Un controllo LoginView viene utilizzato con l'insieme di proprietà LoggedInTemplate per visualizzare contenuto aggiuntivo. Gli utenti anonimi della pagina Web possono eseguire un callback per ottenere informazioni di inventario, mentre gli utenti connessi possono anche eseguire un callback per ottenere informazioni di vendita. Il callback per le informazioni di vendita viene registrato per la convalida dell'evento solo se l'utente è autenticato. Ciò impedisce l'esecuzione del callback da parte di utenti che non sono autenticati.
Esempio
Descrizione
Nell'esempio seguente una pagina Web emula una ricerca del database per determinare il numero di elementi disponibili e se un elemento è in vendita. Per semplificare l'esempio l'archivio dati è rappresentato da due elenchi dizionario. In un'applicazione di produzione, verrebbe invece utilizzato un database. Nell'esempio viene illustrato uno scenario in cui la convalida di callback client impedisce a un utente anonimo di eseguire un callback destinato solo per l'esecuzione da parte di utenti autenticati.
Codice
<%@ Page Language="VB" AutoEventWireup="false"
CodeFile="ClientCallback.aspx.vb" Inherits="ClientCallback" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" >
<title>ASP.NET Example</title>
<script type="text/javascript">
function ReceiveServerData(rValue)
{
Results.innerText = rValue;
}
</script>
</head>
<body>
<form id="form1" >
<div>
<asp:ListBox id="ListBox1" ></asp:ListBox>
<br />
<br />
<button id="LookUpStockButton" onclick="LookUpStock()">Look Up Stock</button>
<asp:LoginView id="LoginView1" >
<LoggedInTemplate>
<button id="LookUpSaleButton" onclick="LookUpSale()">Look Up Back Order</button>
</LoggedInTemplate>
</asp:LoginView>
<br />
Item status: <span id="Results"></span>
</div>
</form>
</body>
</html>
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="ClientCallback.aspx.cs" Inherits="ClientCallback" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML
1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" >
<title>ASP.NET Example</title>
<script type="text/javascript">
function ReceiveServerData(rValue)
{
Results.innerText = rValue;
}
</script>
</head>
<body>
<form id="form1" >
<div>
<asp:ListBox id="ListBox1" ></asp:ListBox>
<br />
<br />
<button id="LookUpStockButton" onclick="LookUpStock()">Look Up Stock</button>
<asp:LoginView id="LoginView1" >
<LoggedInTemplate>
<button id="LookUpSaleButton" onclick="LookUpSale()">Look Up Back Order</button>
</LoggedInTemplate>
</asp:LoginView>
<br />
Item status: <span id="Results"></span>
</div>
</form>
</body>
</html>
Partial Class ClientCallback
Inherits System.Web.UI.Page
Implements System.Web.UI.ICallbackEventHandler
Protected catalog As ListDictionary
Protected saleitem As ListDictionary
Protected returnValue As String
Protected validationLookUpStock As String = "LookUpStock"
Protected validationLookUpSale As String = "LookUpSale"
Sub Page_Load(ByVal sender As Object, ByVal e As _
System.EventArgs) Handles Me.Load
Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
validationLookUpStock, "function LookUpStock() { " & _
"var lb = document.forms[0].ListBox1; " & _
"var product = lb.options[lb.selectedIndex].text; " & _
"CallServer(product, ""LookUpStock"");} ", True)
If (User.Identity.IsAuthenticated) Then
Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
validationLookUpSale, "function LookUpSale() { " & _
"var lb = document.forms[0].ListBox1; " & _
"var product = lb.options[lb.selectedIndex].text; " & _
"CallServer(product, ""LookUpSale"");} ", True)
End If
Dim cbReference As String
cbReference = "var param = arg + '|' + context;" & _
Page.ClientScript.GetCallbackEventReference(Me, _
"param", "ReceiveServerData", "context")
Dim callbackScript As String = ""
callbackScript &= "function CallServer(arg, context) { " & _
cbReference & "} ;"
Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), _
"CallServer", callbackScript, True)
' Populate List Dictionary with invented database data
catalog = New ListDictionary()
saleitem = New ListDictionary()
catalog.Add("monitor", 12)
catalog.Add("laptop", 10)
catalog.Add("keyboard", 23)
catalog.Add("mouse", 17)
saleitem.Add("monitor", 1)
saleitem.Add("laptop", 0)
saleitem.Add("keyboard", 0)
saleitem.Add("mouse", 1)
ListBox1.DataSource = catalog
ListBox1.DataTextField = "key"
ListBox1.DataBind()
End Sub
Public Sub RaiseCallbackEvent(ByVal eventArgument As String) _
Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent
Dim argParts() As String = eventArgument.Split("|"c)
If ((argParts Is Nothing) OrElse (argParts.Length <> 2)) Then
returnValue = "A problem occurred trying to retrieve stock count."
Return
End If
Dim product As String = argParts(0)
Dim validationaction = argParts(1)
Select Case validationaction
Case "LookUpStock"
Try
Page.ClientScript.ValidateEvent("LookUpStockButton", validationaction)
If (catalog(product) Is Nothing) Then
returnValue = "Item not found."
Else
returnValue = catalog(product).ToString() & " in stock."
End If
Catch
returnValue = "Can not retrieve stock count."
End Try
Case "LookUpSale"
Try
Page.ClientScript.ValidateEvent("LookUpSaleButton", validationaction)
If (saleitem(product) Is Nothing) Then
returnValue = "Item not found."
Else
If (Convert.ToBoolean(saleitem(product))) Then
returnValue = "Item is on sale."
Else
returnValue = "Item is not on sale."
End If
End If
Catch
returnValue = "Can not retrieve sale status."
End Try
End Select
End Sub
Public Function GetCallbackResult() _
As String Implements _
System.Web.UI.ICallbackEventHandler.GetCallbackResult
Return returnValue
End Function
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
Page.ClientScript.RegisterForEventValidation("LookUpStockButton", _
validationLookUpStock)
If (User.Identity.IsAuthenticated) Then
Page.ClientScript.RegisterForEventValidation("LookUpSaleButton", _
validationLookUpSale)
End If
MyBase.Render(writer)
End Sub
End Class
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class ClientCallback : System.Web.UI.Page,
System.Web.UI.ICallbackEventHandler
{
protected System.Collections.Specialized.ListDictionary catalog;
protected System.Collections.Specialized.ListDictionary saleitem;
protected String returnValue;
protected String validationLookUpStock = "LookUpStock";
protected String validationLookUpSale = "LookUpSale";
protected void Page_Load(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
validationLookUpStock, "function LookUpStock() { " +
"var lb = document.forms[0].ListBox1; " +
"var product = lb.options[lb.selectedIndex].text; " +
@"CallServer(product, ""LookUpStock"");} ", true);
if (User.Identity.IsAuthenticated)
{
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
validationLookUpSale, "function LookUpSale() { " +
"var lb = document.forms[0].ListBox1; " +
"var product = lb.options[lb.selectedIndex].text; " +
@"CallServer(product, ""LookUpSale"");} ", true);
}
String cbReference = "var param = arg + '|' + context;" +
Page.ClientScript.GetCallbackEventReference(this,
"param", "ReceiveServerData", "context");
String callbackScript;
callbackScript = "function CallServer(arg, context)" +
"{ " + cbReference + "} ;";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"CallServer", callbackScript, true);
catalog = new System.Collections.Specialized.ListDictionary();
saleitem = new System.Collections.Specialized.ListDictionary();
catalog.Add("monitor", 12);
catalog.Add("laptop", 10);
catalog.Add("keyboard", 23);
catalog.Add("mouse", 17);
saleitem.Add("monitor", 1);
saleitem.Add("laptop", 0);
saleitem.Add("keyboard", 0);
saleitem.Add("mouse", 1);
ListBox1.DataSource = catalog;
ListBox1.DataTextField = "key";
ListBox1.DataBind();
}
public void RaiseCallbackEvent(String eventArgument)
{
string[] argParts = eventArgument.Split('|');
if ((argParts == null) || (argParts.Length != 2))
{
returnValue = "A problem occurred trying to retrieve stock count.";
return;
}
string product = argParts[0];
string validationaction = argParts[1];
switch (validationaction)
{
case "LookUpStock":
try
{
Page.ClientScript.ValidateEvent("LookUpStockButton", validationaction);
if (catalog[product] == null)
{
returnValue = "Item not found.";
}
else
{
returnValue = catalog[product].ToString() + " in stock.";
}
}
catch
{
returnValue = "Can not retrieve stock count.";
}
break;
case "LookUpSale":
try
{
Page.ClientScript.ValidateEvent("LookUpSaleButton", validationaction);
if (saleitem[product] == null)
{
returnValue = "Item not found.";
}
else
{
if (Convert.ToBoolean(saleitem[product]))
returnValue = "Item is on sale.";
else
returnValue = "Item is not on sale.";
}
}
catch
{
returnValue = "Can not retrieve sale status.";
}
break;
}
}
public String GetCallbackResult()
{
return returnValue;
}
protected override void Render(HtmlTextWriter writer)
{
Page.ClientScript.RegisterForEventValidation("LookUpStockButton",
validationLookUpStock);
if (User.Identity.IsAuthenticated)
{
Page.ClientScript.RegisterForEventValidation("LookUpSaleButton",
validationLookUpSale);
}
base.Render(writer);
}
}
Commenti
La pagina Web emula una ricerca nel database per determinare il numero di elementi disponibili, o presenti in magazzino, per una serie di prodotti (monitor, tastiere e così via). Per semplificare questo esempio di codice, il database è rappresentato da un elenco dizionario che contiene un piccolo gruppo di elementi. Per ciascun elemento nella tabella, la chiave indica il nome dell'elemento (ad esempio, monitor), mentre il valore indica il numero di elementi presenti in magazzino. In un'applicazione di produzione, verrebbe invece utilizzato un database.
Durante l'esecuzione della pagina, il controllo ListBox è associato alla tabella hash in modo che il controllo ListBox possa visualizzare l'elenco di prodotti. Per gli utenti autenticati, viene eseguito il rendering della pagina con due elementi <button> HTML i cui eventi onclick sono associati a una funzione client denominata LookUpStock e a una funzione client denominata LookUpSale, rispettivamente. Per gli utenti anonimi, il rendering della pagina viene eseguito con un solo elemento <button> HTML, il cui evento onclick è associato alla funzione LookUpStock. Un controllo LoginView viene utilizzato per specificare quali pulsanti devono essere visualizzati. In un evento Render sottoposto a override per la pagina, i pulsanti vengono registrati per la convalida. Se l'utente non è autenticato, il pulsante che avvia il callback per LookUpSale non è registrato e il callback avrà esito negativo se eseguito.
La pagina code-behind aggiunge uno script sul lato client alla pagina mediante il metodo RegisterClientScriptBlock. Lo script che viene aggiunto alla pagina comprende una funzione denominata CallServer che ottiene il nome del metodo che eseguirà il postback al server dal metodo GetCallbackEventReference.
Il callback client richiama il metodo RaiseCallbackEvent che determina la disponibilità in magazzino del prodotto richiesto. Il metodo GetCallbackResult restituisce il valore. Si noti che lo script client e il codice lato server possono scambiare argomenti solo come stringhe. Per passare o ricevere più valori è possibile concatenare valori nella stringa di input o restituita, rispettivamente.
Nota sulla sicurezza: |
---|
Se i callback client e della pagina Web sono legati alla visualizzazione di dati riservati o a operazioni che inseriscono, aggiornano o eliminano dati, è consigliabile convalidare i callback per garantire che il callback venga eseguito dall'elemento dell'interfaccia utente previsto. |
Vedere anche
Attività
Procedura: implementare callback in pagine Web ASP.NET
Concetti
Implementazione di callback client a livello di codice senza postback nelle pagine Web ASP.NET
Esempio di implementazione di callback client (C#)
Esempio di implementazione di callback client (Visual Basic)