Condividi tramite


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)

Riferimenti

ClientScriptManager

RegisterForEventValidation

ValidateEvent