Asp.net中Postback及Callback

咱們知道,在默認的狀況下,當咱們點擊Asp.net Page中的一個服務器Button時(默認實際上是Submit Form),會致使Page被Recreated,這個過程咱們稱之爲Postback,它是Page生命週期的一個階段。咱們將從如下幾個方面來簡單談談Asp.net中的Postback:javascript

  1. 爲何使用Postback
  2. Postback工做過程
  3. 爲何使用Callback
  4. Callback工做過程
  5. Postback與Callback的區別
  6. 參考資料

1.爲何使用Postback

當咱們每次經過瀏覽器在向服務器請求一個Page時,因爲http是無狀態協議,對於服務器來講,都是一個新的請求。然而,在有的時候,咱們但願在新請求中可以保存上一次請求頁面狀態,這正是Postback的由來。那頁面狀態是如何保持的呢,這裏引入了ViewState機制,工做原理大體以下:一個請求到達服務器後,最終,服務器在Render Html時,會將頁面全部EnableViewState屬性爲true的服務器Control狀態序列化,而後經過默認的SHA1算法(可在Config中配置)加密輸出,輸出內容是在中,當點擊頁面按鈕時,會調用生成的會將表單和__VIEWSTATE隱藏域提交給Form,服務器端Page在Load的時候經過IsPostBack屬性判斷出來是PostBack請求,就會將__VIEWSTATE的值反序列化來設置控件狀態,最後將當前頁面狀態Render到Html,這是一個流程。html

2.Postback工做過程

首先,咱們來舉個簡單的例子: 建立一個以下的Aspx頁面:前端

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm4.aspx.cs" Inherits="_2PostBack.WebForm4" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
        <asp:DropDownList ID="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="true">
            <asp:ListItem Text="鄭州" Value="1"></asp:ListItem>
            <asp:ListItem Text="洛陽" Value="2"></asp:ListItem>
        </asp:DropDownList>
        <asp:Button ID="Button1" runat="server" Text="Button1" OnClick="Button1_Click" OnCommand="Button1_Command" CommandArgument="button1"/>
        <asp:Button ID="Button2" runat="server" Text="Button2" OnClick="Button2_Click" OnCommand="Button1_Command" CommandArgument="button2" UseSubmitBehavior="false"/>
    </form>
</body>
</html>
View Code

 Code behind代碼:java

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace _2PostBack
{
    public partial class WebForm4 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            this.Label1.Text = "111";
            this.TextBox1.Text = "222";
        }

        protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.Label1.Text = this.DropDownList1.SelectedItem.Text + "被選中";
        }

        protected void Button2_Click(object sender, EventArgs e)
        {
            //this.ClientScript.RegisterClientScriptBlock(this.GetType(), "", "alert('button2 is clicked');", true);
        }

        protected void Button1_Command(object sender, CommandEventArgs e)
        {
            this.ClientScript.RegisterClientScriptBlock(this.GetType(), "", "alert('Command event is fired by "+e.CommandArgument+"');", true);
        }
    }
}
View Code

 查看頁面源:web

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>

</title></head>
<body>
    <form method="post" action="WebForm4.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__LASTFOCUS" id="__LASTFOCUS" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="Vqv+KjPUckg+144xpf/QLmbVZMyUllu8bC/2XIXx6m9nl9zU1fvBY2vEvG3+yItJZ0KxnR2bBZArLGD6aZmvDpyNsXkmR0lOhuBb9IhlRm8VeVo1Sf3K8ZE7bCTXrM8C" />
</div>

<script type="text/javascript">
//<![CDATA[
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 class="aspNetHidden">

    <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="46C3663D" />
    <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="sIYZq5CKr84HI9Xl83lk+dbfP8dmxk8DsHHFopi45JLSqIGDkhpRjkagLQ64HpyIxKgJvaWNcKeczV3RwU7GSqQ4YCljmXzlle4LuISbIketSy8wozEbj27ERYlEe2aOmcZMsY59JL2OmfM9UyJ2MvLDJZTXP9nZZn1c/iL+tnMYR3PA9U0aGwXUUIS3vfcsDPXcyr2qvn9ncjj2wIyV3GkytI9DqsErncURMoKdVKs=" />
</div>
        <span id="Label1">Label</span>
        <input name="TextBox1" type="text" id="TextBox1" />
        <select name="DropDownList1" onchange="javascript:setTimeout(&#39;__doPostBack(\&#39;DropDownList1\&#39;,\&#39;\&#39;)&#39;, 0)" id="DropDownList1">
    <option selected="selected" value="1">鄭州</option>
    <option value="2">洛陽</option>

</select>
        <input type="submit" name="Button1" value="Button1" id="Button1" />
        <input type="button" name="Button2" value="Button2" onclick="javascript:__doPostBack(&#39;Button2&#39;,&#39;&#39;)" id="Button2" />
    </form>
</body>
</html>
View Code

 會發現生成部分有這樣幾種比較特別的東西:ajax

1.Hidden Input算法

__EVENTTARGET:觸發Event的Control的Unique name;瀏覽器

__EVENTARGUMENT:Event Handler定義的參數;服務器

__LASTFOCUS:這個通常是Changed事件定義;asp.net

__VIEWSTATE:ViewState;

2.Script

定義了一個__doPostBack function來提交表單,以eventTarget和eventArgument爲參數。

3.Control

Button默認被Render成Submit Button,當UseSubmitBehavior設爲false時,是經過Script來提交的,內容是一致的。 看了頁面源,大概也能明白Postback是如何工做的:在Client端經過提交一些參數__EVENTTARGET、__EVENTARGUMENT等,無論是直接提交仍是經過腳本,在Server端經過提交的__EVENTTARGET值反射出該Control,而後判斷該Control是否實現了IPostBackEventHandler接口,若是實現了該接口,當前Page就會調用RaisePostBackEvent方法。

[EditorBrowsable(EditorBrowsableState.Advanced)] 
protected virtual void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument) { sourceControl.RaisePostBackEvent(eventArgument); }  

第一個參數就是Postback的Source control,第二個參數其實就是__EVENTARGUMENT值。 咱們這裏具體指的是Button,先來看下Button定義:

[DefaultEvent("Click"), DefaultProperty("Text"), Designer("System.Web.UI.Design.WebControls.ButtonDesigner, System.Design, Version=4.0.0.0, Culture=neutral, 
PublicKeyToken=b03f5f7f11d50a3a
"), DataBindingHandler("System.Web.UI.Design.TextDataBindingHandler, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"),
SupportsEventValidation, ToolboxData("<{0}:Button runat=\"server\" Text=\"Button\">")]
public class Button : WebControl, IButtonControl, IPostBackEventHandler{}

IPostBackEventHandler接口定義以下:

public interface IPostBackEventHandler { void RaisePostBackEvent(string eventArgument); } 

RaisePostBackEvent方法在Button的實現以下:

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));
}

這樣調用頁面的RaisePostBackEvent方法其實仍是調用了Button的RaisePostBackEvent方法,在Button的RaisePostBackEvent方法具體實現中,先進行驗證,而後觸發Click,接着觸發Command,執行相應的事件處理,而後Render Html,工做過程大體這樣。另外,須要注意的是:當將Label和TextBox的EnableViewState設爲false(默認爲true)後,依次點擊button一、button2,發現Label值最終變爲初始值,而TextBox值不變,緣由是在提交表單時,無論是否啓用ViewState,TextBox都會提交,而Label不會;DropDownList的AutoPostBack屬性默認爲false,此時SelectedItem改變時不會觸發Changed事件,而設爲true後,SelectedItem改變會回發,最終觸發Changed事件,緣由是DropDownList實現了IPostBackDataHandler接口。

3.爲何使用Callback

在Asp.net中客戶端與服務端的交互默認是整頁面提交(包括自動生成的Hidden Input),這無疑加劇了數據傳輸負擔,加大的服務端的工做壓力,並且用戶還須要等待最終處理結果。在開發過程當中,一個很常見的功能是:在用戶註冊時,當用戶輸完用戶名,文本框失去焦點就應該提示用戶名是否可用。該功能的實現目前主要是經過兩種手段,一是純javascript,二是經過.net類庫。在.net類庫中經常使用的有微軟的Asp.net Ajax技術以及第三方的AjaxPro類庫。然而,在這些.net類庫沒有出現以前,使用的是什麼方法呢?就是咱們所要說的Callback,它減輕了數據傳輸負擔,緩解了服務端的工做壓力,而且具備異步性。

4.Callback工做過程

客戶端回調本質上就是指經過前端的客戶端腳本向服務器端傳遞相應的數據參數,服務器端再以接受到的參數進行查詢和處理,最後將結果回傳到客戶端進行顯示。 asp.net 2.0提供了實現無刷新回調的接口ICallbackEventHandler.爲了實現客戶端回調,你必須實現一個 ICallbackEventHandler接口,該接口定義了兩個方法法RaiseCallbackEvent和GetCallbackResult. RaiseCallbackEvent()從瀏覽器接受一個字符串做爲事件參數,即該方法接受客戶端JavaScript傳遞的參數,注意它是首先觸發 的。接下來觸發的就是GetCallbackResult()方法,它將所獲得的結果傳回給客戶端的JavaScript,JavaScript再將結果 更新到頁面。咱們來使用Callback實現上面提到的用戶註冊失去焦點判斷用戶是否已存在。

Aspx代碼:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="_3Callback.WebForm1" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <script type="text/javascript">
        function Success(args, context){
            spanUsername.innerText = args;
        }
        function Error(args, context) {
            spanUsername.innerText = "發生異常";
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <table>
            <tr>
                <td>用戶名:</td>
                <td>
                    <input type="text" id="tbUsername" onblur="CallServerMethod(tbUsername.value,null);"/>
                </td>
                <td>
                    <span style="color:red;">*</span>
                    <span id="spanUsername"></span>
                </td>
            </tr>
            <tr>
                <td>密碼:</td>
                <td>
                    <input type="text" id="tbPassword" />
                </td>
                <td>
                    <span style="color:red;">*</span>
                    <span id="spanPassword"></span>
                </td>
            </tr>
        </table>
    </div>
    </form>
</body>
</html>
View Code

Code Behind代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace _3Callback
{
    public partial class WebForm1 : System.Web.UI.Page,ICallbackEventHandler
    {
        private ClientScriptManager csm;
        private string result;
        protected void Page_Load(object sender, EventArgs e)
        {
            csm = this.Page.ClientScript;//獲取當前頁的ClientScriptManager
            //獲取回調引用。會在客戶端生成DoCallback腳本方法,調用它來實現異步調用
            //
            string reference = csm.GetCallbackEventReference(this, "args", "Success", "", "Error", false);
            string callbackScript = "function CallServerMethod(args,context){\n" + reference + ";\n}";
            csm.RegisterClientScriptBlock(this.GetType(), "CallServerMethod", callbackScript, true);
        }

        public string GetCallbackResult()
        {
            return result;
        }

        public void RaiseCallbackEvent(string eventArgument)
        {
            if (eventArgument == "jello")
                result = eventArgument + "已存在";
            else
                result = eventArgument + "可用";
        }
    }
}
View Code

這裏讓當前Page實現了ICallbackEventHandler接口,由它來處理客戶端回調。

工做流程大體以下:在頁面Load的時候,獲取回調引用,會在客戶端生成DoCallback腳本方法,調用它來實現異步調用,而後註冊須要在客戶端直接調用的腳本並在客戶端調用,在頁面加載完後,首先會調用WebForm_InitCallback()對頁面一些標籤作不一樣處理,當操做致使客戶端事件觸發時將調用WebForm_DoCallback腳本方法,這倆個方法是在生成的axd文件中,查看該文件發現,其實WebForm_DoCallback方法也是經過javascript ajax處理的,當回調成功時,將GetCallbackResult()返回值做爲第一個參數,將context做爲第二個參數並調用該成功回調方法。

5.Postback與Callback的區別

1.Postback本質是一次Submit Form的過程,而Callback本質是一次javascript ajax的過程

2.Postback經過ClientScriptManager.GetPostBackEventReference(Control control, string argument)獲取引用從而生成__doPostback客戶端方法,而Callback經過ClientScriptManager.GetCallbackEventReference(Control control, string argument, string clientCallback, string context, bool useAsync)獲取引用從而生成Webform_DoCallback客戶端方法

3.其它待總結

6.參考資料

1.TRULY Understanding ViewState

2.asp.net viewstate詳解

3.asp.net 中AJAX回調模式(ICallbackEventHandler)

相關文章
相關標籤/搜索