Yan-Feng

记录经历、收藏经典、分享经验

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ASP.net 添加了"event validation"的功能, ASP.NET会检查 POST方法中的所带的参数,如果
认为不合法,就会抛出异常,信息如下

    Invalid postback or callback argument.
        Event validation is enabled using <pages enableEventValidation="true"/> in
    configuration or <%@ Page EnableEventValidation="true" %> in a page.
        For security purposes, this feature verifies that arguments to postback or
    callback events originate from the server control that originally rendered them.
        If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation
    method in order to register the postback or callback data for validation.
   
这个设计的目的是为了防止恶意用户利用post 方法发送一些恶意数据.但是有时一些常见的
case也会出错,比如使用客户端脚本,根据页面上的其他控件的内容来改变一个dropdown list的内容,最常见的case
就是省市县3级联动菜单.又比如在页面上添加一个dropdown list,然后给它添加3个item,再使用客户端脚本在
dropdown list中添加一个item,如果dropdown list的AutoPostBack="True",每次选择list item都会引起postback,
如果所选的item为dropdown list本来就有的,一切正常.如果所选的item是通过客户端脚本添加的,就会出现异常.

在asp.net render DropDownList 时,会遍历DropDownList的item,并记录所有可能的postback的值,其算法为
hash(DropDownList’s UniqueID XOR hash(ListItem’s Value property)),计算的结果会被保存在page中,

<input type="hidden"
       name="__EVENTVALIDATION"
       id="__EVENTVALIDATION"
       value="/wEWBQKGg9abDQKd9sHMBgKc9s…….."
/>
这个过程发生在control的Render()方法中
当页面postback时,ASP.NET会根据这个隐藏值检查postback values,如果找不到对应信息,就会报错

结局方案
1. 禁止这个功能, 但同时会失去一些安全保障:
//—-通过web.config
<system.web>
   <pages enableEventValidation="false"/>
</system.web>
//—-针对某个page
<%@ Page EnableEventValidation="false" … %>

2.Register For Event Validation
其原理就是让asp.net记录这个postback value.
RegisterForEventValidation必须在render时调用.

protected override void Render(HtmlTextWriter writer)
{
   ClientScript.RegisterForEventValidation(_recipeList.UniqueID,"4");
   base.Render(writer);
}

如果我们自己写了一个control,需要使用validate events功能,就需要使用SupportsEventValidation attribute,

[SupportsEventValidation]
public class DynamicDropDownList : DropDownList
{
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
     Page.ClientScript.RegisterForEventValidation(this.UniqueID, "4");
     base.Render(writer);
}
}

目前,asp.net还不能单独禁止某个control的validate events功能.

 

事例:从产品表中读取数据,绑定到repeater中,并将相关数据赋值给repeater中的相关控件.
遇到问题:数据库中存在字段:sort (int类型) 值[1商务系列";2休闲系列;3运动系列;4牛仔系列;](即读取的是数字),但是,客户要求显示时,却要现实汉字种类.

实现:在后台代码中添加一个public方法,该方法实现将相应的数字转化为汉字,方法如下:

    public string GetState(string i)
    {
        if (i == "1")
            return "商务系列";
        if (i == "2")
            return "休闲系列";
        if (i == "3")
            return "运动系列";
        if (i == "4")
            return "牛仔系列";
        else
            return "其他";
    }

然后在前台aspx中调用该方法:

<td><%# GetState(DataBinder.Eval(Container.DataItem, "sort").ToString())%> </td>

 

1 先在Repeater中写一个OnItemCommand
   <asp:Repeater ID="Repeater" runat="server" OnItemCommand="op_ItemCommand" >
2 再在删除那个按钮写一个CommandName
<asp:LinkButton runat="server" ID="zssc" ToolTip="删除发布页面" CommandName ="Del" OnClientClick="return confirm('确定要删除新闻的发布页面?')"></asp:LinkButton>
3 CS代码中写下面的东东
只用把ID绑定在一个隐藏的LABLE上面就可以了,或者你就绑定在一个Lable上面,显示给用户看到他的ID,然后在第3步“//操作代码”那个地方,你写上这么一句:

Lable lb_Repeater_ID = (Label)e.Item.FindControl("lb_ID");

解释:lb_Repeater_ID是让你在下面删除这个记录的时候用的,而后面那个lb_ID是前面页面上,你所点击的那行的控件的名字,那么你要删除的时候,就可以使用lb_Repeater_ID了,见下面:
SqlCommand cmd = new SqlCommand("delete from Table1 where id = '"+ lb_Repeater_ID.Text + "'", conn);


protected void op_ItemCommand(object sender, RepeaterCommandEventArgs e)
    {      
        switch (e.CommandName)
        {           
            case "Del":
                //操作的代码!
                break;
         }
    }

 

 

我们在用vs2005做数据绑定的时候运行出来的
回发或回调参数无效。在配置中使用 <pages enableEventValidation="true"/> 或在页面中使用 <%@ Page EnableEventValidation="true" %> 启用了事件验证。出于安全目的,此功能验证回发或回调事件的参数是否来源于最初呈现这些事件的服务器控件。如果数据有效并且是预期的,则使用 ClientScriptManager.RegisterForEventValidation 方法来注册回发或回调数据以进行验证。

有人说改成pages enableEventValidation="false",但这样我们绑定的控件就用不了了。
我在网上查了一下,只要我们把绑定代码写在IsPostBack里就好了。
因为如果不家判断,我们在点击绑定控件时数据绑定会重做,这样我们原来绑定的东西就不存在了,在回调时就会出错


补充:
ASP.NET中有Event Validation(事件验证)的机制,该机制出于安全性考虑,对回送或回调的控件事件进行验证,以确保该事件由原始控件激发,并阻止可能的通过POST方法传递的恶意数据。在实践当中,有时会因为自己的不慎而引发该异常,下面以一个简单的案例说明其原理和解决方法。Sample.aspx

<asp:GridView ID="GridView1" runat="server">
    <Columns>
        <aspemplateField>
            <ItemTemplate>
                <asp:Button runat="server" Text="Button" />
            </ItemTemplate>
        </aspemplateField>
    </Columns>
</asp:GridView>Sample.aspx.cspublic partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        GridView1.DataSource = Enumerable.Range(0, 5);
        GridView1.DataBind();
    }
}

 

在用户单击GridView1中按钮的时候将会引发上述异常,原因是:(1)点击该按钮前GridView1已经绑定了数据,且Sample.aspx中隐藏字段__EVENTVALIDATION记录了GridView1及其子控件的某种哈希值,以供事件验证机制分析安全性;(2)点击该按钮激发页面回送,这将再次执行Page_Load;(3)Page_Load的再次执行使得GridView1被重新绑定,Sample.aspx页面中__EVENTVALIDATION的值被重新计算,且计算结果与(1)中的值不同,因此ASP.NET的事件验证机制认为用户POST了不安全的数据,继而触发上述异常。(4)事实上,上述逻辑并非POST了不安全数据,而是重复绑定导致__EVENTVALIDATION在点击GridView1中按钮的前后不同而已。解决方法很简单,既然是因为重复绑定导致的,且该异常与Postback有关,因此可修改Code behind为:

Sample.aspx.cspublic partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
          GridView1.DataSource = Enumerable.Range(0, 5);
          GridView1.DataBind();
        }
    }
}

保证了__EVENTVALIDATION前后一致,也就不会遇到那个异常了。网上还有朋友遇到了这个异常,有的是因为出现了上例那样的问题,有的是因为使用Ajax出现问题,无论那种原理都是一样的。网上盛传的两种解决这个问题的方法也顺便列在这里,在某些情况下可以解决问题,但并非每次都管用,比如对于上例就不管用了,因此必须分析导致__EVENTVALIDATION不同的原因才好修复。(1)将页面Page指令的EnableEventValidation设为false,即<%@ Page EnableEventValidation="false" … %>或为web.config添加<system.web>
  <pages enableEventValidation="false"/>
</system.web>这种方法可以暂时屏蔽该异常,但是损失了安全性,且会导致一些控件的事件的响应失灵,因此应当慎用。(2)重写页面的Render方法,并为特定的控件注册事件验证protected override void Render(HtmlTextWriter writer)

{
  Button btn = GridView1.FindControl("Button") as Button;
  this.Page.ClientScript.RegisterForEventValidation(btn.UniqueID);
  base.Render(writer);
}

 

这种方法比(1)好些,但并非每次都能起作用
最近才学习dotNET Framework 2.0的东西,往DataList控件的<ItemTemplate>添加一个<asp:Button />,运行点击按钮出现以下异常

回发或回调参数无效。在配置中使用 <pages enableEventValidation="true"/> 或在页面中使用 <%@ Page EnableEventValidation="true" %> 启用了事件验证。出于安全目的,此功能验证回发或回调事件的参数是否来源于最初呈现这些事件的服务器控件。如果数据有效并且是预期的,则使用 ClientScriptManager.RegisterForEventValidation 方法来注册回发或回调数据以进行验证。

用LinkButton就不会出现这样的情况,我尝试在网上找解决办法,不过还是未能让人满意,大多都是把EnableEventValidation="false",但设置成false会让Button无反应,而且还没有局部的EventValidation,所以把EnableEventValidation设置成false不是首选解决办法。

凭我有限而且不专业的知识和不准确的理解,异常的出现都是“出于安全目的,此功能验证回发或回调事件的参数是否来源于最初呈现这些事件的服务器控件”这句话所描述的原因,所以我觉得,只要给这些“不明”的Button给予“身份”,大概就能解决这个问题了

举个例子吧,有如下一个DataList

<asp:DataList ID="DataList1" runat="server" OnItemDataBound="DataList1_ItemDataBound"
                OnItemCommand="DataList1_ItemCommand">
                <ItemTemplate>
                    <asp:Button ID="Button1" runat="server" Text='<%# Eval("CompanyName") %>' OnClick="Button1_Click" CommandName="test" />
                </ItemTemplate>
</asp:DataList>


通过在Datalist对数据进行DataBind的时期对各Item的Button控件进行PostBackOptions的设置(就是给予这些Button“身份”),例子如下:

protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
    {
        if (e.Item.ItemType == ListItemType.Item ||
             e.Item.ItemType == ListItemType.AlternatingItem)
        {
            PostBackOptions pbo = new PostBackOptions(e.Item.FindControl("Button1"));
            pbo.ActionUrl = "dl.aspx?page="+e.Item.ItemIndex.ToString();  // 回发的目标 URL
            pbo.PerformValidation = true;  // 指示在回发事件发生之前是否需要在客户端进行验证
            pbo.AutoPostBack = false;
            pbo.RequiresJavaScriptProtocol = true;  // 指示是否为客户端脚本生成了 javascript: 前缀
            ((Button)e.Item.FindControl("Button1")).PostBackUrl = Page.ClientScript.GetPostBackEventReference(pbo);   //给予Button“身份”
        }
    }

 
用上面的方法都不影响Button的Click事件和DataList的ItemCommand事件。由于个人的不专业和知识的不牢固,如有错漏请及时指出,就算是被破口大骂我也乐意接受,你们的挑剔就是我的成功之母

posted on 2010-09-14 19:30  Yan-Feng  阅读(823)  评论(0编辑  收藏  举报