Asp.net中Postback及Callback

我们知道,在默认的情况下,当我们点击Asp.net Page中的一个服务器Button时(默认其实是Submit Form),会导致Page被Recreated,这个过程我们称之为Postback,它是Page生命周期的一个阶段。我们将从以下几个方面来简单谈谈Asp.net中的Postback:

  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,这是一个流程。

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>

Code behind代码:

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

查看页面源:

<!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('__doPostBack(\'DropDownList1\',\'\')', 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('Button2','')" id="Button2" />
    </form>
</body>
</html>

会发现生成部分有这样几种比较特别的东西:

1.Hidden Input

__EVENTTARGET:触发Event的Control的Unique name;

__EVENTARGUMENT:Event Handler定义的参数;

__LASTFOCUS:这个一般是Changed事件定义;

__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)后,依次点击button1、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>

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 + "可用";
        }
    }
}

这里让当前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)

Asp.net中Postback及Callback

时间: 2024-10-09 15:10:06

Asp.net中Postback及Callback的相关文章

深入理解asp.net中的 __doPostBack函数

前段时间做一个.net网站的时候,用到了模拟前端按钮刷新updatePanel进行局部刷新的时候,遇见了这个问题,当时没顾上记下来,查看网上资料,记下来留着以后查看. 很早以前,当我刚接触asp.NET开发时,我曾有很多很多的疑问(大概是因为我以前一直做jsp开发,也接触过一段时间的asp,脑海中没有这种基于“控件编程”和“事件编程“模型的缘故吧.当然,如果对于一个长期从事桌面程序开发的人,转型做asp.net--webform当然很轻松.).当我面对这些功能强大的控件,这些属性,这些事件...

ASP.NET中引用dll“找不到指定模块&quot;的完美解决办法 z

DllImport是System.Runtime.InteropServices命名空间下的一个属性类,其功能是提供从非托管DLL导出的函数的必要调用信息.DllImport属性应用于方法,要求最少要提供包含入口点的dll的名称.DllImport的定义如下: [AttributeUsage(AttributeTargets.Method)] public class DllImportAttribute: System.Attribute { public DllImportAttribute

asp.net中利用JSON进行增删改查中运用到的方法

//asp.net中 利用JSON进行操作, //增加: //当点击"增加链接的时候",弹出增加信息窗口,然后,在窗体中输入完整信息,点击提交按钮. //这里我们需要考虑这些:我会进行异步提交,使用jquery中的方法,$.post("网页名",JSON,callback); //JSON的写法:{"name":name,"id":id},那我们对其进行假设,比方说,表单中的textbox很多,需要我们填写的信息 //也很多,

asp.net中的窗口弹出实现,包括分支窗口 . ASP.NET返回上一页面实现方法总结 .

返回上一页的这个东东在我们做项目的时候一般是用于填写完表单后确认的时候,有对原来输入的数据进行修改或者更新时用的,或者是因为网站为了方便浏览者而有心添加的一个东东,一般这种功能的实现在ASP.NET中都是用一个Button控件来实现的,实现的方法有很多,今天恰好在做项目时碰到要用这个东东,我就把能实现" 返回上一页","返回前一页"的几种方法总结了一下,供大家学习之用,请多多指教: 其实要实现这个功能主要还是要用到javascript脚本语言! 方法一: 在asp.

Asp.net中的ViewState用法

基本理论: session值是保存在服务器内存上,那么,可以肯定,大量的使用session将导致服务器负担加重. 而viewstate由于只是将数据存入到页面隐藏控件里,不再占用服务器资源,因此, 我们可以将一些需要服务器"记住"的变量和对象保存到viewstate里面. 而sesson则只应该应用在需要跨页面且与每个访问用户相关的变量和对象存储上. 另外,session在默认情况下20分钟就过期,而viewstate则永远不会过期. 数据类型: 但viewstate并不是能存储所有的

asp.net中打开新窗口的多种方法(转载)

asp.net中打开新窗口的多种方法 1.Response.Redirect("XXX.aspx",true)——直接转向新的页面,原窗口被代替; 2. Response.Write("<script>window.open(XXX.aspx'',''_blank'')</script>")——原窗口保留,另外新增一个新页面; 3.Response.Write("<script>window.location=XXX.a

ASP.NET 中 POST 数据并跳转页面(译自 Redirect and POST in ASP.NET)

本文翻译自 Samer Abu Rabie 的 <Redirect and POST in ASP.NET> 简介 在实际项目中,我们会遇到这样一种应用场景:我们需要与第三方的应用程序通信,在某些特定的情形下,我们不得不使用 POST 请求而非 GET 请求传递信息. 背景 起初,这个问题看起来很简单,但实际并不是那样.我很努力地寻求这个问题的解决方法,仔细谷歌搜索之后发现并没有让我满意的较好解决方案.让我们先来看看向指定的目的URL发送数据(包括POST和GET)有哪些可选方法: 1. Re

asp.net中如何防止用户重复点击提交按钮

asp.net中如何防止用户重复点击提交按钮 asp.net 中防止因为网速慢等影响交互的问题导致用户可能点击多次提交按钮,从而导致数据库中出现多条重复的记录,经过亲自验证在网上找的方法,找到两个切实可行的方法:第一种方法1.页面前台按钮: <asp:Button ID="btnSumbit" runat="server" Text="提交" onclick="btnSumbit_Click" /> 在后台Page

ASP.NET中IsPostBack属性研究

通过页面的IsPostback属性,可以检查 .aspx 页是否为传递回服务器的页面:当加载页面并对控件的更改属性处理之前,用户可以在page_Load事件中检查该页面是否被传递回的页面. 一般是在page_load中检查是否是第一次加载本页或是判断是否是用户提交(PostBack)if (!IsPostBack) {//do something}在用DataGrid访问和更新数据库时没有注意到这个问题,会出现各种各样的奇怪问题,比如我的这种. 问题描述:用DataGrid访问和更新数据库(SQ