天天看點

ASP.NET 回發詳解

相信很多開發過AJAX的朋友都遇到過回發和回調的問題,今天我們就撇開AJAX來單獨談一談回發,并通過一組簡單的示例進行釋疑。我們先在頁面上添加一個用戶端連結控件,設定其Id為“aLink”,看起來應該像這樣:

1:<a id="aLink" onclick="__doPostBack('aLink','[aLink_EventArg]')>aLink</a>      

儲存并在浏覽器中檢視,點選連結後系統會提示腳本錯誤,原因很簡單,檢視頁面源代碼就知道,目前不存在__doPostBack這個回發函數,這就與調用了一個不存在的Javascript函數一樣。

先把這個問題放在一邊,接下來我們在頁面上添加一個伺服器端按鈕控件,設定其Id為“btn1”,并将其onClientClick屬性設定為一個回發函數,看起來應該像這樣:

1:<asp:Button ID="btn1" runat="server" Text="Btn1" OnClientClick="__doPostBack('btn1','[btn1_EventArg]')" />      

儲存并在浏覽器中檢視,點選以後出現和剛才一樣的腳本錯誤,原因之前已經說了,同時說明.NET Framework不會為伺服器端控件自動生成回發函數。

我們把這個問題也放在一邊,接下來再在頁面上添加一個伺服器端按鈕控件,這次在前端不需要設定額外的屬性,是以它看起來應該像這樣:

1:<asp:Button ID="btn2" runat="server" Text="Btn2" />      

這還沒完,我們需要切換到代碼視圖,并重寫Page的呈現方法,看起來應該像這樣:

1:protected override void Render(HtmlTextWriter writer) { 
2: this.btn2.OnClientClick += ClientScript.GetPostBackEventReference(this.btn2, "[btn2_EventArg]", true); 
3: base.Render(writer); 
4:}      

GetPostBackEventReference方法的作用是傳回一段用于用戶端事件的字元串,以觸發到伺服器端的回發。接收的第一個參數是用來處理回發的控件,第二個是用來處理額外事件資訊的字元串形式參數,第三個是是否注冊事件引用以進行驗證。跟蹤這個方法所傳回的字元串就會發現,它與我們為Btn1所寫的回發字元串别無二緻。儲存這些修改并在浏覽器中檢視,我們驚喜地發現Btn2可以正常工作(盡管光從頁面源代碼比較Btn1和Btn2無法看出有什麼差別),更加令人驚喜的是,假如你在IE下進行調試(筆者的IE版本為IE8),那麼會發現連之前不能工作的兩個控件也運作地很好。其實原因是因為GetPostBackEventReference不僅僅傳回一段用來觸發回發的字元串,而且在伺服器端注冊了回發事件,在頁面的源代碼中我們可以看到系統自動生成了__doPostBack函數。但如果你還裝有其它的浏覽器(Chrome或是Firefox),那麼用它們來打開頁面,你就會發現點選Btn1将會使.NET Framework抛出一個異常,具體内容如下:

ASP.NET 回發詳解

.NET Framework出于安全考慮需要為伺服器端控件的回發和回調事件進行注冊,因為Btn1沒有進行過類似注冊(Btn2使用GetPostBackEventReference進行了注冊),是以會抛出異常,這也正是aLink不會引起異常的原因(因為它是用戶端控件)。你可以手動關閉對回發和回調的驗證,方法是将Page或是Web.config中的EnableEventValidation屬性設定為False(如果兩者設定有沖突,以Page的為準),看起來應該像這樣:

1:<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ClientPostback.aspx.cs" Inherits="WebApplication1.ClientPostback"  EnableEventValidation="false" %>      

或是:

1:<system.web> 
2: <pages enableEventValidation ="false"></pages> 
3:</system.web>      

但是這種無視安全性的做法是不值得推薦的,除非回發或回調的事件是不可預測的,否則應該為每一個回發或回調事件進行注冊。要想為Btn1進行注冊,需要回到代碼視圖,在之前的頁面呈現方法中添加注冊(注冊這一行為隻能寫在Page.Render中,同時需要注意注冊的控件唯一辨別和參數必須與前端保持一緻),修改後的Render看起來應該像這樣:

1:protected override void Render(HtmlTextWriter writer) { 
2: Page.ClientScript.RegisterForEventValidation(this.btn1.UniqueID,"[btn1_EventArg]"); 
3:    this.btn2.OnClientClick += ClientScript.GetPostBackEventReference(this.btn2, "[btn2_EventArg]", true); 
4:    base.Render(writer); 
5:}      

現在即使我們在非IE浏覽器下觸發回發事件也能夠正常運作了。目前Btn1和Btn2所包含的代碼可以說是等價的,但要想進行回發還有更加簡便的方法。

再在頁面中添加一個伺服器端按鈕控件,将其Id設定為“btn3”,并将它的UseSubmitBehavior屬性設定為False,看起來應該像這樣:

1:<asp:Button ID="btn3" runat="server" Text="Btn3" UseSubmitBehavior="False" onclick="btn3_Click" />      

UseSubmitBehavior的預設屬性是True,這意味着點選按鈕預設将會觸發浏覽器的送出操作,而設定成False意味着點選按鈕将會觸發.NET Framework的回發操作,請注意觸發源和觸發行為的不同。設定了UseSubmitBehavior屬性以後系統将會自動為其進行注冊,注冊使用的控件唯一辨別是控件的UniqueID,參數為空字元串。

接下來我們再在頁面中添加一個伺服器端按鈕,将其Id設定為“btn4”(好吧,我承認用數字進行排序的Id過不了多久就會讓人産生迷茫,不過作為一個小例子,我想把更多的精力放在叙述的流暢度上而不是絞盡腦汁為它們起一個優雅的名字),并将其onClientClick設定為“__doPostBack('btn3','')”,看起來應該像這樣:

1:<asp:Button ID="btn4" runat="server" Text="Btn4" OnClientClick="__doPostBack('btn3','')" />      

儲存并在浏覽器中檢視,點選Btn4會發現,雖然我們沒有進行注冊,但是它運作地很好,這說明回發事件的注冊與伺服器控件本身的ID無關,隻要保證回發事件中的UniqueID被注冊過(Btn3被系統自動注冊,前面已經說到過),就能成功進行調用。

如果你在btn3_Click中對sender進行捕捉并且足夠細心,你就會發現回發和送出的最大不同:如果通過送出進入,那麼sender将是觸發送出的那個按鈕;如果通過回發進入,那麼sender将是ID與回發函數中的UniqueID對應的那個按鈕。也就是說,在Click方法中,後端無法知道到底是誰真正觸發了它,因為你跨過了浏覽器,直接向伺服器端發送了請求。