【案例】
某個ASP.NET WebForm網頁,加入JavaScript動態修改欄位,送出表單時出現錯誤:
(英文版) 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.
(中文版) 無效的回傳或回呼引數。已在組態中使用 <pages enableEventValidation="true"/> 或在網頁中使用 <%@ Page EnableEventValidation="true" %> 啟用事件驗證。基於安全性理由,這項功能驗證回傳或回呼引數是來自原本呈現它們的伺服器控制項。如果資料為有效並且是必需的,請使用 ClientScriptManager.RegisterForEventValidation 方法註冊回傳或回呼資料,以進行驗證。
追查後發現問題出在JavaScript為<asp:DropDownList>動態新增了選項。
ASP.NET 2.0+為避免原本Server端管控的下拉選單被駭客加料塞入非預期值,故DropDownList會記下原有選項組合,一旦Client讓該欄位送回選項以外的值,便會觸發錯誤。解決之道是透過RegisterForEventValidation()方法向ASP.NET預告該欄位可能出現的值,如此資料送回時比對吻合就能通過驗證。
用一個範例來說明: WebForm網頁上有一個DropDownList,預先宣告C#及VB.NET兩個ListItem,另外再透過JavaScript加入Ruby及JavaScript兩個新<option>。至於Server端,我們需覆寫Render()方法,加入ClientScript.RegisterForEventValidation(),但此處只註冊JavaScript,以觀察Ruby及JavaScript兩個選項產生的結果。程式碼如下:
<%@ Page Language="C#" %>
<!DOCTYPEhtml>
<scriptrunat="server">
protectedvoid btn_Click(object sender, EventArgs e)
{
Response.Write(ddl.SelectedValue + " " + Request["ddl"]);
Response.End();
}
//為了讓Cient新增的DropDownList選項被接受,Page需覆寫Render方法
//註冊前端可能動態加入的新選項
protectedoverridevoid Render(HtmlTextWriter writer)
{
ClientScript.RegisterForEventValidation(
ddl.UniqueID, "JavaScript");
base.Render(writer);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Test DropDownList Change in Client Side</title>
<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.0.min.js"></script>
<script >
$(function () {
//動態增加兩個新選項,注意: 選Ruby送出會出錯,選JavaScript卻OK
$("#ddl")
.append("<option value='Ruby' selected>Ruby</option>")
.append("<option value='JavaScript'>JavaScript</option>");
});
</script>
</head>
<body>
<formid="form1"runat="server">
<asp:DropDownListID="ddl"runat="server">
<asp:ListItem>C#</asp:ListItem>
<asp:ListItem>VB.NET</asp:ListItem>
</asp:DropDownList>
<asp:ButtonID="btn"runat="server"OnClick="btn_Click"Text="Submit"/>
</form>
</body>
</html>
執行結果如上圖,下拉選單會出現四個選項,選Ruby按Submit會出錯(如下圖);選C#、VB.NET、JavaScript按Submit則不會出錯。
但是還有一個問題: 選取JavaScript雖然不會出錯,Request["ddl"]也能取得選取結果--"JavaScript",但透過ddl.SelectedValue查到的卻是C#,表示DropDownList.Selected*屬性只接受Server端建立的選項,應用時需留意此一限制。
如此看來,若只是為了在Server端及Client端都能增減下拉選單選項,可以不用DropDownList,改用<select runat="server">更簡單,且後端取值應以Request["…"]為準。
<%@ Page Language="C#" %>
<!DOCTYPEhtml>
<scriptrunat="server">
protectedvoid btn_Click(object sender, EventArgs e)
{
Response.Write(Request["ddl"]);
Response.End();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Test DropDownList Change in Client Side</title>
<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.0.min.js"></script>
<script >
$(function () {
//動態增加兩個新選項
$("#ddl")
.append("<option value='Ruby' selected>Ruby</option>")
.append("<option value='JavaScript'>JavaScript</option>");
});
</script>
</head>
<body>
<formid="form1"runat="server">
<selectname="ddl"id="ddl"runat="server">
<option>C#</option>
<option>VB.NET</option>
</select>
<asp:ButtonID="btn"runat="server"OnClick="btn_Click"Text="Submit"/>
</form>
</body>
</html>