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.
首先,這個錯誤源自ASP.NET 2.0所加入的新功能,主要的著眼於防範資料在傳送前被駭客篡改,以強化資安。例如: 你有個下拉選單,決定會員要加到哪個群組,如果駭客戶得知還有個系統管理者群組,偷偷把它加進選項裡,那那那... (不可否認,這類防護挺煩人的,但很不幸地,"用麻煩換資安"一向是鐵律,唉~~~)
我最常看到的情境,多半都是開發者用AJAX或純Javascript方式,依使用者的需求動態變更了下拉選單的內容,接著在送出表單時,噹! Exception~~ 這都肇因於ASPX不認得這些Client-Side動態加入的下拉選項,抱著寧可錯殺一百不可錯放一個的心態,質疑這是遭駭客篡改的結果,用Exception阻擋"入侵"。
<%@ Page Language="C#" AutoEventWireup="true"%>
<html><head><title>Event Validation Test</title></head>
<script type="text/C#" runat="server">
protected void Page_Load(object sender, EventArgs e)
{ if (!Page.IsPostBack)
{ DropDownList1.Items.Add(new ListItem("1")); DropDownList1.Items.Add(new ListItem("2")); }
}
</script>
<body onload="init();">
<form id="form1" runat="server">
<div>
<asp:DropDownList ID="DropDownList1" runat="server">
</asp:DropDownList>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
<script type="text/javascript">
function init() { var sel = document.getElementById("DropDownList1"); sel.options[sel.options.length] = new Option("3"); }
</script>
</form>
</body>
</html>
在上面的例子中,我們在Server-Side為DropDownList1加上選項1, 2,在Client-Side以Javascript加上選項3。實際執行時,如果你選1, 選2,按Button1時一切正常,若選3再按Button1,就會見到前述的Exception。問題出在ASP.NET並不認得我們在Client-Side另行加入的選項3。
除了修改enableEventValidation停用整個網頁的事件檢核防護,錯誤訊息中提到的另一個做法是用ClientScriptManager.RegisterForEventValidation讓ASP.NET認得"3"這個在Client-Side偷生的小孩,寫法如下:
<script type="text/C#" runat="server">
protected void Page_Load(object sender, EventArgs e)
{ if (!Page.IsPostBack)
{ DropDownList1.Items.Add(new ListItem("1")); DropDownList1.Items.Add(new ListItem("2")); }
}
//在Render()加入特別申報邏輯
protected override void Render(HtmlTextWriter writer)
{ //另外為"3"報戶口
ClientScript.RegisterForEventValidation(
DropDownList1.UniqueID,
"3");
//不要忘了呼叫原來的Render()
base.Render(writer);
}
</script>
經過這番修改,選3後再按Button1便不再出錯。不過你應該也會注意到了,Postback後,下拉選單並不會留在3上,畢竟在ASP.NET的認知中,DropDownList1的選項只有1, 2而已。
以上的做法,雖然可以讓"3"就地合法,但如果選項內容是透過AJAX在執行期間呼叫其他Web Page或Web Service取得,在開發ASPX時根本無從得知,則此一解決方式豈不淪為空談? 沒錯! 用EnableEventValidation排除無關資安的情境看來較為最簡便,但每次一設就是整個Web Page,難道不能只針對某些會動態變更的Server Control設定? 不行!