Clientrückruf und Beispiel für die Implementierung einer Validierung
Aktualisiert: November 2007
Bei einem Clientrückruf sendet eine Clientskriptfunktion eine Anforderung an die ASP.NET-Webseite. Diese führt dann eine Kurzversion des normalen Lebenszyklus aus, um den Rückruf zu bearbeiten. Um sicherzustellen, dass Rückrufereignisse von der erwarteten Benutzeroberfläche stammen, können Sie Rückrufe überprüfen. Bei einer Rückrufvalidierung wird ein Ereignis während der Wiedergabe der Webseite für die Validierung registriert und dann während des Rückrufs validiert.
Hinweis: |
---|
Mit der Ereignisvalidierung können Webanwendungen vor falschen Postbacks, jedoch nicht vor wiederholten Angriffen geschützt werden. Ein etwas umfassenderes Ereignisvalidierungsschema sollte auch die Besonderheiten der Webanwendung und die Berechtigungen der Benutzer berücksichtigen, die auf die Ressourcen zurückgreifen. Weitere Informationen finden Sie unter Sicherheit für ASP.NET-Webanwendungen. |
Im hier erläuterten Beispiel werden Beispielimplementierung eines Clientrückrufs (C#) und Beispiel für das Implementieren von Clientrückrufen (Visual Basic) erweitert. In diesen Beispielen handelt es sich bei dem ListBox-Steuerelement ListBox1 um ein Steuerelement des Servers, mit dem eine Produktliste angezeigt wird. Ein HTML <button>-Element (nicht ein Button-Serversteuerelement) führt einen Rückruf aus, um Produktbestandsdaten abzurufen. Das Beispiel wird so erweitert, dass zusätzliche Informationen zum Verkaufstatus eines Produkts bereitgestellt werden und diese Informationen nur von authentifizierten Benutzern angezeigt werden dürfen. Ein LoginView-Steuerelement wird verwendet, bei dem die LoggedInTemplate-Eigenschaft auf die Anzeige weiterer Inhalte festgelegt ist. Anonyme Benutzer der Webseite können einen Rückruf ausführen, um Bestandsdaten abzurufen. Angemeldete Benutzer dagegen können über einen Rückruf auch Verkaufsinformationen abrufen. Der Rückruf für die Verkaufsinformationen wird nur dann für die Ereignisvalidierung registriert, wenn der Benutzer authentifiziert ist. So wird verhindert, dass nicht authentifizierte Benutzer einen Rückruf ausführen können.
Beispiel
Beschreibung
Im folgenden Beispiel emuliert eine Webseite eine Datenbanksuche, um die Anzahl der verfügbaren Artikel sowie die Verfügbarkeit eines Artikels zu ermitteln. Um das Beispiel zu vereinfachen, wird der Datenspeicher durch zwei Wörterbuchlisten dargestellt. In einer Betriebsanwendung würde stattdessen eine Datenbank verwendet werden. In diesem Beispiel wird ein Szenario dargestellt, bei dem durch Überprüfen von Clientrückrufen verhindert wird, dass ein anonymer Benutzer einen Rückruf ausführt, der nur für authentifizierte Benutzer vorgesehen ist.
Code
<%@ 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);
}
}
Kommentare
Die Webseite emuliert eine Datenbanksuche, um für einige Produkte (Monitore, Tastaturen usw.) die Anzahl der verfügbaren oder vorrätigen Artikel zu bestimmen. Um das Codebeispiel zu vereinfachen, wird die Datenbank als Wörterbuchliste dargestellt, die nur einige wenige Elemente enthält. Für jeden Artikel in der Tabelle ist der Artikelname der Schlüssel (z. B. Monitor), und der Wert beschreibt die Anzahl der vorrätigen Artikel. In einer Betriebsanwendung würde stattdessen eine Datenbank verwendet werden.
Beim Ausführen der Seite wird ein ListBox-Steuerelement an die Hashtabelle gebunden, damit das ListBox-Steuerelement die Produktliste anzeigen kann. Für authentifizierte Benutzer wird die Seite mit zwei HTML <button>-Elementen gerendert, deren onclick-Ereignisse an eine Clientfunktion mit dem Namen LookUpStock bzw. an eine Clientfunktion mit dem Namen LookUpSale gebunden sind. Für anonyme Benutzer wird die Seite mit nur einem HTML <button>-Element gerendert, dessen onclick-Ereignis an LookUpStock gebunden ist. Ein LoginView-Steuerelement wird verwendet, um anzugeben, welche Schaltflächen angezeigt werden. In einem überschriebenen Render-Ereignis für die Seite werden die Schaltflächen für die Validierung registriert. Wenn der Benutzer nicht authentifiziert ist, wird die Schaltfläche, die den Rückruf für LookUpSale auslöst, nicht registriert und ein Rückrufversuch schlägt fehl.
Die Code-Behind-Seite fügt der Seite über die RegisterClientScriptBlock-Methode Clientskripts hinzu. Das hinzugefügte Skript enthält die CallServer-Funktion, die von der GetCallbackEventReference-Methode den Namen der Methode abruft, die das Postback an den Server ausführt.
Der Clientrückruf ruft die RaiseCallbackEvent-Methode auf, die den verfügbaren Bestand für den an sie übergebenen Artikel ermittelt. Die GetCallbackResult-Methode gibt den Wert zurück. Beachten Sie, dass es sich bei den Argumenten, die zwischen Clientskript und Servercode hin und her gesendet werden, nur um Zeichenfolgen handeln darf. Sie können mehrere Werte gleichzeitig eingeben oder empfangen, wenn Sie in der Eingabezeichenfolge bzw. in der Ausgabezeichenfolge jeweils Werte verknüpfen.
Sicherheitshinweis: |
---|
Wenn die Webseite und die Clientrückrufe vertrauliche Daten anzeigen oder mit Operationen arbeiten, die Daten einfügen, aktualisieren oder löschen, sollten Sie die Rückrufe unbedingt überprüfen, um sicherzustellen, dass das dafür vorgesehene Element der Benutzeroberfläche den Rückruf ausführt. |
Siehe auch
Aufgaben
Gewusst wie: Implementieren von Rückrufen in ASP.NET-Webseiten
Konzepte
Programmgesteuertes Implementieren von Clientrückrufen ohne Postbacks in ASP.NET-Webseiten
Beispielimplementierung eines Clientrückrufs (C#)
Beispiel für das Implementieren von Clientrückrufen (Visual Basic)