【茶包射手日記】防止 WebForm 重複 PostBack 與 UpdatePanel
0 |
今年初為解決 ASP.NET WebForm 在 IE 會重複 PostBack 問題,我想到在 Form onsubmit 事件加入控制旗標的解法,實測能有效預防內含 AutoPostBack 欄位 WebForm 的重複 PostBack 行為。近日接到同事報案,說他嘗試套用後,會讓某些表單無法送單。
比對分析後,發現問題與 UpdatePanel 有關。(年輕朋友們應該不知道 UpdatePanel 是什麼鬼,但當年它可是讓不懂 JavaScript 的 .NET 工程師也能寫出 AJAX 動態網頁的黑魔法呢?只是後來 JavaScript 成了人人都要會的基本技能,靠著傳送龐大資料實現魔法的它就開始被嫌棄還慢慢臭掉了...)
原本的寫法是在表單送出時設定 document.body.isPosting = true,之後若重複送單也觸發 onsubmit 事件就會被擋下。而完成 PostBack 後,頁面刷新,body.isPosting 會被重設成 false,允許送單。
var body = document.body;
body.isPosting = false;
document.getElementById("form1").onsubmit = function() {
if (body.isPosting) return false;
body.isPosting = true;
return true;
};
問題來了,UpdatePanel 更新時也會觸發 onsubmit,但只更新 UpdatePanel 範圍非整頁刷新,body.isPosting 維持 true,阻擋了所有後續動作。
先修改上次文章的範例,加入 UpdatePanel 重現問題:
<%@Page Language="C#"%>
<script runat="server">
//TODO: 示範用,忽略記憶體清除作業
static Dictionary<string, string> data = new Dictionary<string, string>();
void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack && Request["__EVENTTARGET"] != "ddlUpdPanelCode")
{
var seq = Request["seq"];
var mode = Request["mode"];
data[seq] = (data.ContainsKey(seq) ? data[seq] : string.Empty) + DateTime.Now.ToString("ssfff") + "\n";
switch (mode)
{
case "Fail":
throw new ApplicationException("啊,挫賽! 玩壞了。");
case "Delay":
System.Threading.Thread.Sleep(3000);
break;
}
Response.ContentType = "text/plain";
Response.Write("*** Result ***\n" + data[seq]);
Response.End();
}
}
protected void ddlUpdPanelCode_SelectedIndexChanged(object sender, EventArgs e)
{
lblUpdPanelMsg.Text = ddlUpdPanelCode.SelectedValue;
}
</script>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=7" />
<style>
input[readonly] { width: 2.5em; color: #666; background-color: #eee }
.upd-panel { background-color: #eee; padding: 3px; margin: 6px; width: 185px; }
.upd-panel > * { font-size: 10pt; display: inline; }
</style>
</head>
<body>
<form id="form1" method="post" runat="server">
<script>
var body = document.body;
body.isPosting = false;
document.getElementById("form1").onsubmit = function() {
if (body.isPosting) return false;
body.isPosting = true;
return true;
};
</script>
<asp:ScriptManager runat="server" />
<input type="text" name="seq" value="<%=Guid.NewGuid().ToString().Substring(0, 4)%>" readonly />
<select name="mode">
<option>Succ</option>
<option>Delay</option>
<option>Fail</option>
</select>
<input type="submit" name="submit_action" value="Submit" />
<asp:DropDownList runat="server" AutoPostBack="True">
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
</asp:DropDownList>
<div class="upd-panel">
<asp:DropDownList ID="ddlUpdPanelCode" runat="server"
AutoPostBack="True"
OnSelectedIndexChanged="ddlUpdPanelCode_SelectedIndexChanged">
<asp:ListItem>Please select...</asp:ListItem>
<asp:ListItem Value="ABC">ABC</asp:ListItem>
<asp:ListItem Value="XYZ">XYZ</asp:ListItem>
</asp:DropDownList>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="lblUpdPanelMsg" runat="server" Text="Code"></asp:Label>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddlUpdPanelCode" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
如以下實測,沒觸發 UpdatePanel 更新前運作如常,重複操作右側 AutoPostBack 下拉及 Submit 鈕也會導致重複 PostBack。但只要動一次下方的 Code 下拉選單觸發 UpdatePanel,之後 Code 下拉選單、Submit 鈕,跟右側的 123 下拉選單全都失去作用。
清朝火砲發射不順,值得花時間檢修嗎?深深吸了一口氣,我勇敢打開 F12 潛進 ASP.NET AJAX JavaScript 原始碼,找到一個神祕狀態變數可用來識別 onsubmit 事件是否來自 UpdatePanel Trigger - Sys.WebForms.PageRequestManager.getInstance()._postBackSettings.async,當它為 true 表示 PostBack 動作來自 UpdatePanel,不要把 body.isPosting 設成 true 就能避開問題。
var body = document.body;
body.isPosting = false;
document.getElementById("form1").onsubmit = function() {
//Ignore UpdatePanel postback
if (Sys.WebForms.PageRequestManager.getInstance()._postBackSettings.async)
return true;
if (body.isPosting) return false;
body.isPosting = true;
return true;
};
修改後,UpdatePanel 就不會跟防重複 PostBack 程式打架囉。
Try to find a way to detect form submit by UpdatePanel.
Comments
Be the first to post a comment