[ASP.NET] 設定 UseSubmitBehaviot 來防止按鈕重複送出&重複點擊方法

摘要:[ASP.NET] 設定 UseSubmitBehaviot 來防止按鈕重複送出&重複點擊方法

前言


  使用者的使用方式總是千奇百怪防不勝防,這次要說的問題就是出現使用者連續點擊兩次以上按鈕的情況,為什麼會有這種情況發生呢? 有可能是使用者手抖了一下、網路的頻寬太小或傳輸的資量太大導致當使用者點擊了第一次按鈕後網頁畫面沒有反應就又去點擊了第二次,當碰到者種情況我們可以使用以下的處理方法來防止按鈕被多次點擊造成資料發生錯誤。

 

  先來看一下 Button 的 UseSubmitBehaviot 屬性,這個屬性是控制 Button 於 HTML 輸出時 HTML Tag 的 Type 屬性是否為 Submit 或 Button,在 ASP.NET 的 Button 控制項中預設是會去執行 PostBack,UseSubmitBehaviot 的預設值為 true,也就是預設的 Button 是會去執行 ASP.NET PostBack 機制,但當 UseSubmitBehaviot 設定為 false 時,將改成執行用戶端瀏覽器的送出機制,也就是 HTML 產生後將會多出一段 Script 為 __doPostBack()  函式。

 

  於HTML原始碼下如以下:

  當 UseSubmitBehavior = true


<input type="submit" name="Button1" value="Button" id="Button1" />

  當 UseSubmitBehavior = false


var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

<input type="button" name="Button" value="Button" onclick="javascript:__doPostBack('Button2','')" id="Button2" />

 

  透過以上編碼可以發現 UseSubmitBehaviot 為 false 時是執行 onClick 呼叫 __doPostBack() 函式,這時可以透過 GetPostBackEventReference() 方法插入一段 Script 返回 Client 端執行,例如加入Confirm 彈出視窗來讓使用者確認或防止使用者重複點擊送出按鈕方法,讓我們接下來看範例說明。

 

範例


以下程式碼為加入防止使用者重複點擊送出按鈕的方法。

Step 1

  於 ASPX 頁面中加入 Label 與 Button 並設定 useSubmitBehaviot = false


<form id="form1" runat="server">
    <asp:Button ID="Button1" runat="server" Text="Submit" />
    <br />
    <asp:Label ID="Label1" runat="server" Text="Message"></asp:Label>
</form>

 

Step 2

  於 Code Behind 中加入以下程式碼,在 Page_Load 時為 Button1 附加 onClick 事件並使用 GetPostBackEventReference() 方法註冊 Script ,這邊做的動作就是附加一段 JavaScript 編碼,當點擊後將 Button 做 Disabled 並更改文字為"儲存中"。


protected void Page_Load(object sender, EventArgs e)
{
    this.Button1.Attributes.Add("onclick",
        ClientScript.GetPostBackEventReference(Button1, "click") +
        ";this.disabled=true; this.value='Saveing...';");
}

  GetPostEventReference(Control, String) : 第一參數帶入 Control ID, 第二個參數帶入事件名稱。

 

  實際上 GetPostEventReference(Button1, "click") 這段程式碼在網頁產生後會變成 " __doPostBack(&#39;Button1&#39;,&#39;click&#39;); " 這樣,而我們做的事情只是在產生的這段 Script 編碼後多附加上鎖定按鈕的 Script 編碼。

 

Step 3

  於 Code Behind 的 Button Click Event 中讓程式 Sleep 3 秒模擬處理大量資料或網路延遲發生。


protected void Button1_Click(object sender, EventArgs e)
{
    System.Threading.Thread.Sleep(3000);
    Label1.Text = "Save OK!";
}

 

Step 4

  完成以上步驟後,實際去執行網頁並點擊 Button 則會發現此時 Button 變成暗色無法點選,如此就可以防止使用者重複點擊送出情形

2011/12/03 補充

  如在 Post Server 端前要綁 JavaScript 驗證時可寫一個驗證 Function 並回傳 true / false,而 Code Behind 註冊 OnClick 時更改為以下。


this.Button1.Attributes.Add("onClick", 
  "if (checkField()) {" + this.GetPostBackEventReference(Button1, "Click") + 
  ";this.disabled=true;this.value='儲存中';this.style.width=60;}else{return false;}");

 

2011/12/25 補充

  以上的寫法看似好像沒有問題,不過實際上在 Windows 事件檢視器會發現每當點擊一次按鈕就會產生一個錯誤訊息,如下。


Event code: 3005 Event message: 發生未處理的例外狀況。 
Event time: 2011/12/25 下午 11:54:33 
Event time (UTC): 2011/12/25 下午 03:54:33 
Event ID: e588a2786881407a957190ecf6fc2f88 Event sequence: 71 
Event occurrence: 7 Event detail code: 0 
.......
....... 
Exception information: Exception type: ArgumentException Exception message: 
無效的回傳或回呼引數。已在組態中使用 或在網頁中使用 <%@ page="" enableeventvalidation="true">啟用事件驗證。
基於安全性理由,這項功能驗證回傳或回呼引數是來自原本呈現它們的伺服器控制項。如果資料為有效並且是必需的,請
使用 ClientScriptManager.RegisterForEventValidation 方法註冊回傳或回呼資料,以進行驗證。 
....... 
.......

 

  原因是因為呼叫了 GetPostBackEventReference() 方法,導致 Button1 與原本的事件驗證脫離,網路上大部分的解法都是說將 EnableEventValidation 屬性設定為  false,關閉 EnableEventValidation 的驗證模式,但是這個方法其實並不恰當,ASP.NET 的事件驗證機制是用來防止惡意程式攻擊,關閉了事件驗證機制等於少了本身的防範機制,會讓網站置於危險的風險下並且有可能導致其他控制項的失效及問題。

 

  所以在這邊我改寫原本的方法,使用 RegisterForEventValidation 將 Button1 重新註冊驗證機制,RegisterForEventValidation 方法必須於 Page_Render 中註冊,第一步我覆寫 Render 事件,在 Render 中註冊 RegisterForEventValidation 及 GetPostBackEventReference ,由於撰寫 Page.ClientScript.RegisterForEventValidation 時,會提示這個方法已經過時,所以現在改使用 ClientScriptManager 類別方法。


protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
	ClientScriptManager cs = Page.ClientScript;
	cs.RegisterForEventValidation(Button1.UniqueID, "click");
	this.btnSave.Attributes.Add("onclick", cs.GetPostBackEventReference(Button1, "click") + ";this.disabled=true;");
	base.Render(writer);
}

 

  RegisterForEventValidation 方法的第一個參數為 Control 的 UniqueID, 第二個參數為事件引數,在 GetPostBackEventReference 方法中我使用 "click",所以在這必須填入一樣的事件引數。

 

  當修改成以上程式碼後,在 HTML 頁中 Button1 的 useSubmitBehaviot 屬性需要設定為 true 或刪除才可以正常執行,如未刪除則會出現重複 PostBack 的情況發生,修改完成後接著測試按鈕在回 Windows 事件檢視器中查看會發現已經無以上的錯誤訊息出現,因為已經使用 RegisterForEventValidation 方法,重新將 Button1 註冊驗證事件。

 

2012/10/11 日補充

  因 Kent 反映以上的方式有無法使用驗證控制項的問題,所以在這邊提供另一種方式的做法,實際將執行時期的 ASPX 網頁使用原始碼開起來看。

  可以看到當頁面要 Submit 的時後會去呼叫 WebForm_OnSubmit() 函式,實際看一下 WebForm_OnSubmit() 在做什麼。

  上圖中可發現,在 Submit 的時後會去呼叫驗證的一些函式,那些驗證控制項的檢查就是在這個時後,而我現在要做的就是當驗證完成後再繼續去執行我的其他方法,加入一個 JavaScript 方法,如下。


    function disableButton() {
        var inputs = document.getElementsByTagName("input");
        for (var i = 0; i < inputs.length; i++) {
            if (inputs[i].type == "button") {
                var btn = inputs[i];
                btn.disabled = true;
            }
        }
    }

 

  這個方法在做的動作就是 Disabled 畫面上所有 INPUT TYPE 為 BUTTON 的元素,送出後不能點擊任何按鈕這個是很合理的,回到 Code Behind 裡,加入以下方法。


    protected override void Render(System.Web.UI.HtmlTextWriter writer)
    {
        ClientScriptManager cs = Page.ClientScript;
        cs.RegisterOnSubmitStatement(this.GetType(), "lockButton", " disableButton(); ");
        base.Render(writer);
    }

 

  我使用 RegisterOnSubmitStatement 方法,這個方法就是當頁面submit的時後,會去執行你所指定的陳述式,也就是我的 disableButton() 這個方法,接下來回到ASPX頁面執行時期的原始碼畫面。

 

  在這可以看到多了一個 disableButton(); 的呼叫,也就是當認證成功的時後,會先執行這個方法將按鍵關閉後才執行return true;,如此我們也就可以達到送出時先驗證後鎖定按鍵的效果了,另外注意一下,ASP.NET Button 控制項 Type 都是 Submit ,所以要加 UseSubmitBehavior="false" 屬性轉換成 Type Button。

 

參考資料


Button.UseSubmitBehavior 屬性

ClientScriptManager.RegisterForEventValidation 方法

ASP魔法學院按鈕加上詢問訊息

http://msdn.microsoft.com/zh-tw/library/system.web.ui.clientscriptmanager.registeronsubmitstatement.aspx

http://www.dotnetodyssey.com/2014/12/29/prevent-double-clicking-multiple-form-submission-asp-net-button-clicked/

 

 


以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教
如有侵權內容也請您與我反應~謝謝您 :)