Share via


Ejemplo de devolución de llamada del cliente con implementación de validación

Actualización: noviembre 2007

En una devolución de llamada del cliente, una función de secuencia de comandos cliente envía una solicitud a la página Web ASP.NET, que a su vez ejecuta una versión abreviada de su ciclo de vida normal para procesar la devolución de llamada. Para garantizar que esos eventos de devolución de llamada se originan desde la interfaz de usuario prevista, puede validar las devoluciones de llamada. La validación de devoluciones de llamada implica registrar un evento para la validación durante la representación de la página Web y después validar el evento durante la devolución de llamada.

Nota:

La validación de eventos permite proteger la aplicación Web contra devoluciones de datos manipuladas, pero no sirve para protegerse de ataques de reproducción. Un plan de validación de eventos más completo debería tener en cuenta las características específicas de su aplicación Web y los permisos del usuario que tiene acceso a sus recursos. Para obtener más información, consulte Seguridad de aplicaciones Web ASP.NET.

El ejemplo que aquí se describe es una ampliación del Ejemplo de implementación de devolución de llamada de cliente (C#) y Ejemplo de implementación de la devolución de llamada del cliente (Visual Basic). En esos ejemplos, un control ListBox denominado ListBox1 es un control del servidor que muestra una lista de productos. Un elemento <button> HTML (no un control de servidor Button) realiza una devolución de llamada para obtener información del inventario del productos. El ejemplo se ha ampliado para incluir información adicional sobre si un producto está en venta y para permitir que esta información sólo la vean los usuarios autenticados. Para mostrar el contenido adicional, se utiliza un control LoginView con la propiedad LoggedInTemplate definida. A los usuarios anónimos de la página Web se les permite ejecutar una devolución de llamada para obtener información del inventario, mientras que a los usuarios registrados se les permite además ejecutar una devolución de llamada para obtener información sobre las ventas. La devolución de llamada de la información de ventas sólo se registra para la validación de eventos si el usuario está autenticado. De esta forma, se evita que los usuarios que no están autenticados ejecuten devoluciones de llamada.

Ejemplo

Descripción

En el ejemplo siguiente, una página Web simula una búsqueda en la base de datos para determinar el número de productos que están disponibles y si un producto está en venta. Para simplificar el ejemplo, el almacén de datos se representa mediante dos listas de diccionario. En una aplicación de producción, se utilizaría una base de datos. En el ejemplo se muestra una situación en la que, al validar las devoluciones de llamadas del cliente, se impide que un usuario anónimo ejecute una devolución de llamada concebida únicamente para los usuarios autenticados.

Código

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

Comentarios

La página Web emula una búsqueda de base de datos para determinar el número de elementos disponibles, o en existencias, para una serie de productos (monitores, teclados, etc.). Para simplificar este ejemplo de código, la base de datos está representada por una lista de diccionario que contiene un pequeño grupo de elementos. Para cada elemento de la tabla, la clave es el nombre del elemento (por ejemplo, monitor) y el valor es el número de elementos que hay en existencias. En una aplicación de producción, se utilizaría una base de datos.

Cuando se ejecuta la página, se enlaza un control ListBox a la tabla hash para que el control ListBox muestre la lista de productos. Para los usuarios autenticados, la página se representa con dos elementos <button> HTML cuyos eventos onclick se enlazan a dos funciones de cliente denominadas LookUpStock y LookUpSale, respectivamente. Para los usuarios anónimos, la página se representa con sólo un elemento <button> HTML, cuyo evento onclick se enlaza a LookUpStock. Se utiliza un control LoginView para especificar los botones que se muestran. En un evento Render reemplazado de la página, los botones se registran para la validación. Si el usuario no está autenticado, no se registra el botón que inicia la devolución de llamada para LookUpSale y cualquier intento de realizar una devolución de llamada producirá un error.

La página de código subyacente agrega secuencias de comandos de cliente a la página a través del método RegisterClientScriptBlock. La secuencia de comandos que se agrega a la página incluye una función denominada CallServer, que obtiene el nombre del método que realiza la devolución de datos al servidor desde el método GetCallbackEventReference.

La devolución de llamada del cliente invoca al método RaiseCallbackEvent, que devuelve las existencias disponibles del producto especificado. El método GetCallbackResult devuelve el valor. Tenga en cuenta que los argumentos enviados entre la secuencia de comandos de cliente y el código del servidor sólo pueden ser cadenas. Para pasar o recibir varios valores, puede concatenar los valores en la cadena de entrada o en la cadena devuelta, respectivamente.

Nota de seguridad:

Si las devoluciones de llamada del cliente y de la página Web están relacionadas con la presentación de datos confidenciales u operaciones que insertan, actualizan o eliminan datos, es recomendable que valide las devoluciones de llamada para garantizar que el elemento de la interfaz de usuario previsto ejecuta la devolución de llamada.

Vea también

Tareas

Cómo: Implementar devoluciones de llamada en páginas Web ASP.NET

Conceptos

Implementar mediante programación devoluciones de llamada de cliente sin devoluciones de datos en páginas web de ASP.NET

Ejemplo de implementación de devolución de llamada de cliente (C#)

Ejemplo de implementación de la devolución de llamada del cliente (Visual Basic)

Referencia

ClientScriptManager

RegisterForEventValidation

ValidateEvent