浅谈ASP.NET的Postback
我們知道,無論是ASP.NET1.x,2.0,甚至是以后的版本,ASP.NET最終Render到Client端通過瀏覽器瀏覽的都是一樣:一個單純的HTML。Client通過Submit Form的方式將填入Form的數據提交給Server進行處理。我們現在來看看ASP.NET整個Postback程序處理的過程。
首先我們通過一個Sample來看ASP.NET如何處理一個通過Click一個Button引起的Postback。下面是Web Page的HTML:
<%@?Page?Language="C#"?AutoEventWireup="true"?CodeFile="Default.aspx.cs"?Inherits="_Default"?%><!DOCTYPE?html?PUBLIC?"-//W3C//DTD?XHTML?1.0?Transitional//EN"?"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html?xmlns="http://www.w3.org/1999/xhtml">
<head?runat="server">
????<title>Test?Page</title>
</head>
<body>
????<form?id="form1"?runat="server">
????????<div>
????????????<asp:Label?runat="server"?ID="LabelMessage"?ForeColor="red"></asp:Label>
????????</div>
????????<div>
????????????<asp:Button?runat="server"?ID="Button1"?Text="Button1"?OnClick="Button1_Click"?OnCommand="Button_Command"?????CommandArgument="Button1"?/>
????????????<asp:Button?runat="server"?ID="Button2"?Text="Button2"?OnClick="Button2_Click"?OnCommand="Button_Command"????CommandArgument="Button2"?UseSubmitBehavior="false"?/>
????????????<asp:Button?runat="server"?ID="Button3"?Text="Button3"?OnClick="Button3_Click"?OnCommand="Button_Command"????CommandArgument="Button3"?UseSubmitBehavior="false"?/>
????????</div>
????</form>
</body>
</html>
很簡單,定義了3個Button,分別注冊了他們的兩個Event:Click和Command。3個Button的Command Event Hander是一樣的:Button_Command,通過指定的CommandArgument來讓Event Handler判斷到底是哪個Button觸發了Command ?Event。
下面是Code Behind:
using?System;using?System.Data;
using?System.Configuration;
using?System.Web;
using?System.Web.Security;
using?System.Web.UI;
using?System.Web.UI.WebControls;
using?System.Web.UI.WebControls.WebParts;
using?System.Web.UI.HtmlControls;
public?partial?class?_Default?:?System.Web.UI.Page
{??
????protected?void?Page_Load(object?sender,?EventArgs?e)
????{
???????
????}
????protected?void?Button1_Click(object?sender,?EventArgs?e)
????{
????????string?message?=?string.Format("The?{0}?event?of?{1}?is?fired",?"Click",?"Button1");
????????this.LabelMessage.Text?=?message;
????}
????protected?void?Button2_Click(object?sender,?EventArgs?e)
????{
????????string?message?=?string.Format("The?{0}?event?of?{1}?is?fired",?"Click",?"Button2");
????????this.LabelMessage.Text?=?message;
????}
????protected?void?Button3_Click(object?sender,?EventArgs?e)
????{
????????string?message?=?string.Format("The?{0}?event?of?{1}?is?fired",?"Click",?"Button3");
????????this.LabelMessage.Text?=?message;
????}
????protected?void?Button_Command(object?sender,?CommandEventArgs?e)
????{
????????string?message?=?string.Format("The?{0}?event?of?{1}?is?fired",?"Command",?e.CommandArgument);
????????this.LabelMessage.Text?+=?";?"?+?message;
????}
}
我們來運行這個Page,并Click某個按鈕(比如Button2):
我們通過最上方的Message可以看出,Button2的Click Event和Command先后觸發。
這篇Blog的主旨就是從方法調用的角度講述整個程序運行的過程:從HTML 被Render到Client端,到用戶Click某個按鈕,輸入被Postback到Server端,并觸發兩個Event,執行Event Handler打印出相關的Message。
首先我們來看看ASP.NET設計的Page Render到Client端的HTML是什么樣子:
<!DOCTYPE?html?PUBLIC?"-//W3C//DTD?XHTML?1.0?Transitional//EN"?"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html?xmlns="http://www.w3.org/1999/xhtml">
????<head>
????????<title>
????????????Test?Page
????????</title>
????</head>
????<body>
????????<form?name="form1"?method="post"?action="Default.aspx"?id="form1">
????????????<div>
????????????????<input?type="hidden"?name="__EVENTTARGET"?id="__EVENTTARGET"?value=""?/>
????????????????<input?type="hidden"?name="__EVENTARGUMENT"?id="__EVENTARGUMENT"?value=""?/>
????????????????<input?type="hidden"?name="__VIEWSTATE"?id="__VIEWSTATE"?value="/wEPDwUKMTA0NDQ2OTE5OWRk281L4eAk7iZT10hzg+BeOyoUWBQ="?/>
????????????</div>
<script?type="text/javascript">
????????????????<!--
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();
????}
}
//?-->
</script>
<div>
<span?id="LabelMessage"?style="color:Red;"></span>
</div>
<div>
????<input?type="submit"?name="Button1"?value="Button1"?id="Button1"?/>
????<input?type="button"?name="Button2"?value="Button2"?onclick="javascript:__doPostBack('Button2','')"?id="Button2"?/>
????<input?type="button"?name="Button3"?value="Button3"?onclick="javascript:__doPostBack('Button3','')"?id="Button3"?/>
</div>
</form>
</body>
</html>
上面的HTMLBody部分大體包括3個部分:
1.?? 定義了3個hidden field:
<input?type="hidden"?name="__EVENTTARGET"?id="__EVENTTARGET"?value=""?/>????????????????<input?type="hidden"?name="__EVENTARGUMENT"?id="__EVENTARGUMENT"?value=""?/>
<input?type="hidden"?name="__VIEWSTATE"?id="__VIEWSTATE"?value="/wEPDwUKMTA0NDQ2OTE5OWRk281L4eAk7iZT10hzg+BeOyoUWBQ="?/>
從他們的命名可以看出他們分別代表的意思:__EVENTTARGET代表觸發Event的Control的Unique name;__EVENTARGUMENT代表為Event Handler定義的額外的參數;__VIEWSTATE:代表的是Viewstate。
2.?? 一段script:
<script?type="text/javascript">????????????????<!--
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();
????}
}
//?-->
</script>
定義了一個__doPostBack function完成Postback的操作,該function只有區區3行代碼,前兩行通過參數對上面定義的兩個hidden?field賦值,然后向Server端提交表單。
3.?? 一段HTML對應通過ASP.NET定義的Web Control。?
<div><span?id="LabelMessage"?style="color:Red;"></span>
</div>
<div>
????<input?type="submit"?name="Button1"?value="Button1"?id="Button1"?/>
????<input?type="button"?name="Button2"?value="Button2"?onclick="javascript:__doPostBack('Button2','')"?id="Button2"?/>
????<input?type="button"?name="Button3"?value="Button3"?onclick="javascript:__doPostBack('Button3','')"?id="Button3"?/>
div>
我們定義的3個Button被轉化成3個能向Server端提交表單的<input > Tag, 但是他們提交表的方式卻不一樣,第一個以<input type="submit">的方式提交,后面兩個通過調用javascript的方式提交表單(<input type="button">)。對于一個System.Web.UI.WebControls.Button,默認采用第一種提交方式,但是我們通過設置UseSubmitBehavior屬性(這個屬性時ASP.NET 2.0新加的,1x沒有相應的設置),改變其表單提交的行為。
當用戶Click Button2的時候,調用__doPostBack,并傳入兩個參數:一個代表出發Event的對象的Unique name,也就是Button2的名稱,另一個描述Event的額外信息的參數,這里不需要,所以這里是空字符串。在__doPostBack中把這兩個參數賦值給兩個Hidden Field:__EVENTTARGET,__EVENTARGUMENT。然后向Server端提交表單,完成Postback。
然后我們來看看Server如何處理這個Postback,關于Web Page的生命周期在這里就不詳細介紹了。Server端通過__EVENTTARGET這個hidden field的值找到對應的Server端的Control,通過Reflection確定該Control是否實現了System.Web.UI.IPostBackEventHandler Interface。如果該Control確實實現了該Interface,那么調用Page的RaisePostBackEvent方法,這是一個Virtual的方法,可以被Override。我們來看該方法的定義。
[EditorBrowsable(EditorBrowsableState.Advanced)]protected?virtual?void?RaisePostBackEvent(IPostBackEventHandler?sourceControl,?string?eventArgument)
{
????sourceControl.RaisePostBackEvent(eventArgument);
}
我們可以看到該方法直接調用該sourceControl的RaisePostBackEvent,并傳入一個eventArgument參數,在這個例子中sourceControl就是__EVENTTARGET對應的Web?Control:Button2,eventArgument就是__EVENTTARGET對應的值:一個空字符串。Button2的類型是System.Web.UI.WebControls.Button。我們來看看System.Web.UI.WebControls.Button中的RaisePostBackEvent方法是如何定義的:
protected?virtual?void?RaisePostBackEvent(string?eventArgument){
????base.ValidateEvent(this.UniqueID,?eventArgument);
????if?(this.CausesValidation)
????{
????????this.Page.Validate(this.ValidationGroup);
????}
????this.OnClick(EventArgs.Empty);
????this.OnCommand(new?CommandEventArgs(this.CommandName,?this.CommandArgument));
}
這個方法也很簡單,先進行Validation,然后先后出發兩個Event:OnClick 和OnCommand,隨后調用對應的Event handler,這和我們的輸出結果是吻合的。
這基本上就是整個Postback的整個程序執行的過程,現在我們對我們的Page作一些小的有趣的改動,來驗證一下:
Client端和Server端進行交互的途徑就是提交表單(Form Submitting),而我們現在有兩種方式來提交表單:通過<input type="submit">控件;通過調用javascript:__doPostBack。基于這一點我們在Html中加了下面一段javascript:?
<script?type="text/javascript">????function?postback()
????{
???????
????????__doPostBack('Button1','');
????}?
???
????document.getElementById("Button2").onclick?=?postback;
????document.getElementById("Button3").onclick?=?postback;?
???</script>
我們override Button2和Button3的onclick event,把'Button1作為參數傳入__doPostBack方法,可以想象,現在無論Click那個Button,程序都將認為之Click Button1。有興趣的可以親自試試,無論Click那個Button,顯示的效果都將是下面的樣子:?
接下來我們取消上面的改動,在Server對Code作另一方面的嘗試。我們前面說過,Server接受到Client的Postback,對于事件的Web Control(或者Html Server Control),如果實現了System.Web.UI.IPostBackEventHandler接口,會調用Page的virtual方法:RaisePostbackEvent,我們現在來Override這個方法:?
????{
????????sourceControl?=?this.Button1;
????????base.RaisePostBackEvent(sourceControl,?eventArgument);
}
在上面的Code中,我們把sourceControl設為Button1,這樣無論在client端Click的那個Button,現在都將認為是對Button的Click。運行的結果和上面一樣。
通過上面的介紹,我們知道了Page的RaisePostBackEvent會調用Source Control的RaisePostBackEvent方法,這個方法是定義在IPostBackEventHandler接口中,很多Control都實現了這個方法,對于Button來說,這個方法是Virtual的,它可以被你Override,如果感興趣的話,可以自己寫一個Custom Button,并Override該方法,看看執行的情況,相信會使加深你對Postback的理解。
總結
以上是生活随笔為你收集整理的浅谈ASP.NET的Postback的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Asp.net控件之异同:HTML控件与
- 下一篇: asp.net控件本质