asp.net夜话之五:Page类和回调技术

在今天我主要要介绍的有如下知识点:
Page类介绍
Page的生命周期
IsPostBack属性
ClientScriptManager类
回调技术(CallBack)

Page类介绍

asp.net有时候也被成为WebForm,因为开发一个asp.net页面就像开发一个WinFrom窗体一样,我们同样可以采用拖拽控件、双击产生相关处理代码的方法。在asp.net中,创建一个页面可以采用两种模型。

单页模型
用Dreamweaver创建的asp.net页面就是单页模型,当然利用Visual Studio 2005也能创建单页模型,不过在Visual Studio 2005中创建的页面默认不是单页模型,要想在Visual Studio 2005创建单页模型的网页如下:

项处于未选中状态,默认情况下这个选项是处于选中状态的。这样就创建了单页模型的网页。
此时的页面代码如下:

注意在页面中有这样一句代码:

  1. <script runat="server">
  2. </script>

这句代码与普通javascript语句块不同的是有一个runat="server"属性,表示这里的代码是在服务器上运行的C#代码。切换到设计视图,然后双击页面,然后这部分会变成如下的样子:

  1. <script runat="server">
  2. protected void Page_Load(object sender, EventArgs e)
  3. {
  4. }
  5. </script>

其中Page_Load就是页面加载的时候在服务器上运行的方法。
单页模型的特点是HTML标记、控件代码及服务器端运行的C#代码全部包含在一个aspx页面中,Web服务器第一次运行该页面的时候会将这个页面生成一个类文件,对于上面的Index.aspx页面,会生成ASP.Index_aspx的类,然后再将这个ASP.Index_aspx类编译成IL代码,Web服务器通过CLR(Common Language Runtime,通用语言运行环境)运行相应的IL代码。
单页模型的缺点是页面和代码混在一起,维护起来较为麻烦。

代码页面分离模式
代码页面模式就是将页的标记(HTML代码)和服务器端元素放在.aspx页面中,而也代码在位于一个.aspx.cs中。采用默认方式创建的aspx网页就是这种方式。
下面就是一个采用代码页面分离模式创建的Home.aspx页面的代码:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs" Inherits="Home" %>
<!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>无标题页</title>
</head>
<body> <form id="form1" runat="server"> <div> </div> </form>
</body>
</html> 

其对应的页代码是:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
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 Home : System.Web.UI.Page
{ protected void Page_Load(object sender, EventArgs e) { }
}

首先要关注的aspx的头部分代码:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs" Inherits="Home" %>

@Page是一个页面指令,在这里Language="C#"指明了当前页面采用的后台代码是C#语言,CodeFile="Home.aspx.cs"表示这个页面对应的页代码文件是Home.aspx.cs这个文件,Inherits="Home"表示当前aspx页继承自Home这个类。

现在再关注一下页代码文件声明:

public partial class Home : System.Web.UI.Page

从这部分代码可以看出Home类是继承自System.Web.UI.Page类的。注意这里还有一个C#2.0的关键字partial,这个关键字表示当前代码是一个局部类,以表示这个类是构成整个Web页面窗体的一部分。Web服务器运行这个页面的时候最终会将aspx页面和对应的页代码编译成一个类文件,然后生成IL代码。

代码页面分离模式的好处是页面展示部分和逻辑控制部分的代码分离开来,便于管理和维护,这也是微软推荐的开发方式。

asp.net页面的声明周期


asp.net页面运行的时候将经历一个声明周期,这个生命周期中会进行一系列的操作,调用一系列的方法。了解asp.net页面的生命周期对于精确控制页面的控件呈现方式和行为非常重要。

一般说来一个常规页面要经历如下几个生命周期阶段:

阶段         说明
页请求 页请求发生在页生命周期开始之前。用户请求页时,ASP.NET 将确定是否需要分析和编译页(从而开始页的生命周期),或者是否可以在不运行页的情况下发送页的缓存版本以进行响应。
开始 在开始阶段,将设置页属性,如 Request 和 Response。在此阶段,页还将确定请求是回发请求还是新请求,并设置 IsPostBack 属性。此外,在开始阶段期间,还将设置页的 UICulture 属性。
页初始化 页初始化期间,可以使用页中的控件,并将设置每个控件的 UniqueID 属性。此外,任何主题都将应用于页。如果当前请求是回发请求,则回发数据尚未加载,并且控件属性值尚未还原为视图状态中的值。
加载 加载期间,如果当前请求是回发请求,则将使用从视图状态和控件状态恢复的信息加载控件属性。
验证 在验证期间,将调用所有验证程序控件的 Validate 方法,此方法将设置各个验证程序控件和页的 IsValid 属性。
回发事件处理 如果请求是回发请求,则将调用所有事件处理程序。
呈现 在呈现期间,视图状态将被保存到页,然后页将调用每个控件,以将其呈现的输出提供给页的 Response 属性的 OutputStream。
卸载 完全呈现页、将页发送至客户端并准备丢弃时,将调用卸载。此时,将卸载页属性(如 Response 和 Request)并执行清理。

在页的生命周期中,一般会有如下事件:

页事件

典型使用
Page_PreInit 使用 IsPostBack 属性确定是否是第一次处理该页。
创建或重新创建动态控件。
动态设置主控页。
动态设置 Theme 属性。
读取或设置配置文件属性值。
注意:如果请求是回发请求,则控件的值尚未从视图状态还原。如果在此阶段设置控件属性,则其值可能会在下一阶段被改写。
Page_Init 读取或初始化控件属性。
Page_Load 读取和更新控件属性。
Control  events     执行特定于应用程序的处理:
如果页包含验证程序控件,请在执行任何处理之前检查页和各个验证控件的 IsValid 属性。
处理特定事件,如 Button 控件的 Click 事件
Page_PreRender 对页的内容进行最后更改。
Page_Unload 执行最后的清理工作,可能包括:
关闭打开的文件和数据库连接。
完成日志记录或其他特定于请求的任务。

需要注意的是,每个asp.net控件也有与asp.net类似的生命周期,如果aspx页面中包含有asp.net服务器控件,那么在调用页面的方法时也会调用控件的相关方法。

另外,Web应用程序是无状态的。每次请求一个新网页或者刷新页面服务器都会创建一个当前页的新实例,这就意味着无法获取页面的以前的信息,如果确实需要这么做,需要采用额外的机制。
我们将刚才新建的Index.aspx页面中添加代码,如下:

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server"> string date; protected void Page_Load(object sender, EventArgs e) { if (date == null)//如果date为空则设置为当前时间的字符串形式 { date = DateTime.Now.ToString(); } Response.Write("当前时间:"+date); }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server"> <title>无标题页</title>
</head>
<body> <form id="form1" runat="server"> <div> </div> </form>
</body>
</html> 

按照正常理解,第一次运行的时候date字符串为null,会被设置成系统当前的字符串表示形式,并且输出,再次刷新的时候date字符串不再为空,会依然输出刚才的时间字符串,但是结果却不是这样。第一次运行的结果:

刷新页面之后的结果:

这就证明了即使是刷新当前页也会重新生成一个当前页面的实例,因为只有在生成页面新实例的情况下date字符串变量才为空,才会被重新设置值。

IsPostBack属性
Page类有一个IsPostBack属性,这个属性用来指示当前页面是第一次加载还是响应了页面上某个控件的服务器事件导致回发而加载。
这次我们继续对Index.aspx页面添加代码,在页面中增加了一个Button控件,如下:

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server"> string date; protected void Page_Load(object sender, EventArgs e) { if (date == null)//如果date为空则设置为当前时间的字符串形式 { date = DateTime.Now.ToString(); } Response.Write("当前时间:"+date); if (!Page.IsPostBack) { Response.Write("第一次加载。"); } else { Response.Write("响应客户端回发而加载。"); } } protected void btnOK_Click(object sender, EventArgs e) { }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server"> <title>无标题页</title>
</head>
<body> <form id="form1" runat="server"> <div> <asp:Button ID="btnOK" runat="server" OnClick="btnOK_Click" Text="提交" /></div> </form>
</body>
</html> 

页面第一次运行的结果:

按一下F5刷新页面的结果:

点击一下“提交”按钮之后的结果:

由此可见每次打开一个页面和刷新一个页面效果都是一样的,只有响应客户端回发时IsPostBack属性才是true。了解这个属性和服务器采用了一种机制来“记录”服务器控件的状态这种做法(其实利用了ViewState和ControlState机制,这部分后续文章中会讲到)对于将来数据绑定会有很大作用。

动态输出javascript脚本
对于Index.aspx页面上面的执行情况,我们看到了满意的结果。我们再来看一下这个页面在客户端生成的HTML代码,在浏览器窗口打开的页面点鼠标右键,然后选择“查看源文件”,HTML代码如下:

当前时间:2008-9-21 0:04:33响应客户端回发而加载。
<!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> 无标题页
</title></head>
<body> <form name="form1" method="post" action="Index.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTY3NzE5MjIyMGRkD2VvJFADDdEHh4W9UfAyzIvI3ss=" />
</div> <div> <input type="submit" name="btnOK" value="提交" id="btnOK" /></div> <div> <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgL8vZamCQLdkpmPAeM33vfm1ARVNKdKAoq5+eQdFI1J" />
</div></form>
</body>
</html>

我们会看到“当前时间:2008-9-21 0:04:33响应客户端回发而加载。”这句话位于<html></html>标记之外。在第一夜时候就提到过,asp.net页面是满足XML标准的HTML语言,但是通过在Page_Load事件中利用Response属性会将文字输出在<html></html>标记之外,不符合XHTML标准。这对于普通页面来说也许并无大碍,但是如果在频繁输出javascript脚本的网页中,可能会对网页的客户端执行效果产生影响。因为javascript脚本块在客户端调用方法之前还是客户端调用方法之后效果可能会不一样。
下面在Home窗体的Page_Load事件中添加代码,如下:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
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 Home : System.Web.UI.Page
{ protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { Response.Write("<script language='javascript'>alert('" + DateTime.Now.ToString() + "')</script>"); } }
}

这样每次运行Home.aspx页面的时候都会弹出一个对话框,如下图:

这不是我们所关心的,我们关注的是生成的HTML代码,如下:

<script language='javascript'>alert('2008-9-21 0:22:52')</script>
<!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> 无标题页
</title></head>
<body> <form name="form1" method="post" action="Home.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGTB6tgIyCoS2q3pZeKmhFwC24pQzw==" />
</div> <div> </div> </form>
</body>
</html>

可以看见输出的javascript代码在<html></html>标记之外。
在Page类中有一个ClientScript属性,它是ClientScriptManager的实例,这个类是在asp.net2.0中新增的。ClientScriptManager有如下几个常用方法:
RegisterClientScriptBlock方法:向 Page 对象注册客户端脚本。
RegisterStartupScript方法:向 Page 对象注册启动脚本。
ClientScriptManager类通过键string和Type来唯一标识脚本。具有相同类型的键和Type的脚本识为同一脚本。
下面对Home窗体的Page_Load事件中输入如下代码:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
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 Home : System.Web.UI.Page
{ protected void Page_Load(object sender, EventArgs e) { if (!ClientScript.IsClientScriptBlockRegistered(this.GetType(), "ClientScriptBlock")) { ClientScript.RegisterClientScriptBlock(this.GetType(), "ClientScriptBlock", "<script language='javascript'>alert('ClientScriptBlock')</script>"); } if (!ClientScript.IsStartupScriptRegistered(this.GetType(), "StartupScript")) { ClientScript.RegisterStartupScript(this.GetType(), "StartupScript", "<script language='javascript'>alert('StartupScript')</script>"); } //Response.Write("<script language='javascript'>alert('" + DateTime.Now.ToString() + "')</script>"); }
}

执行该页面时,会弹出两个提示窗口,生成的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> 无标题页
</title></head>
<body> <form name="form1" method="post" action="Home.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGTB6tgIyCoS2q3pZeKmhFwC24pQzw==" />
</div>
<script language='javascript'>alert('ClientScriptBlock')</script> <div> </div> <script language='javascript'>alert('StartupScript')</script></form>
</body>
</html>

可以看出上面的两个方法输出的javascript脚本都在<form></form>标记之内,不会破环文章的结构,而且RegisterClientScriptBlock方法输出的javascript脚本代码块靠近<form>标记的开始标记,而RegisterStartupScript方法输出的javascript脚本代码块靠近<form>标记的结束标记,了解这一点对于控制动态添加的客户端脚本的时间是非常有利的。

回调技术(CallBack)

在asp.net中客户端与服务器端的交互默认都是整页面提交,此时客户端将当前页面表单中的数据(包括一些自动生成的隐藏域)都提交到服务器端,服务器重新实例化一个当前页面类的实例响应这个请求,然后将整个页面的内容重新发送到客户端,这种处理方式对运行结果没什么影响,不过这种方式加重了网络的数据传输负担、加大了服务器的工作压力,并且用户还需要等待最终处理结果。假如是我们希望有这么一个功能,当用户填写完用户名之后就检查服务器数据库里是否已存在该用户名,如果存在就给出已经存在此用户名的提示,如果不存在就提示用户此用户名可用,对于这种情况其实只需要传递一个用户名作为参数即可,上面的做法却需要提交整个表单,有点小题大做。解决上面的问题的办法目前主流做法有三种:纯javascript实现、微软Ajax类库实现还有用AjaxPro实现。后两种做法在稍后的文章中会讲到,这里我讲另外一种实现:通过回调技术。

创建实现回调技术的网页与普通asp.net网页类似,只不过还需要做以下特殊工作:
(1)让当前页面实现ICallbackEventHandler接口,这个接口定义了两个方法:string GetCallbackResult ()方法和void RaiseCallbackEvent (string eventArgument)方法。其中GetCallbackResult ()方法的作用是返回以控件为目标的回调事件的结果,RaiseCallbackEvent()方法的作用是处理以控件为目标的回调事件。

(2)为当前页提供三个javascript客户端脚本函数。一个javascript函数用于执行对服务器的实际请求,在这个函数中可以提供一个字符串类型的参数发送到服务器端;另一个javascript函数用于接收服务器端方法的执行后返回的字符串类型结果,并处理这个结果;还有一个是执行对服务器请求的帮助函数,在服务器代码中通过GetCallbackEventReference()方法获取这个方法的引用时由asp.net自动生成这个函数。
下面我以一个详细的例子来讲述如何使用回调,用Dreamweaver创建一个Register. aspx页面,代码如下:

<%@ Page Language="C#" ContentType="text/html" ResponseEncoding="gb2312" %>
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
<%@ Import Namespace="System.Text" %>
<!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>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>用户注册</title>
<script language="javascript">
//客户端执行的方法
//下面的方法是接收并处理服务器方法执行的返回结果
function Success(args, context)
{ message.innerText  = args;
}
//下面的方式是当接收服务器方法处理的结果发生异常时调用的方法
function Error(args, context)
{ message.innerText  = '发生了异常';
}
</script>
<script language="c#" runat="server">
string result="";
// 定义在服务器端运行的回调方法.
public void RaiseCallbackEvent(String eventArgument)
{ if(eventArgument.ToLower().IndexOf("admin")!=-1) { result=eventArgument+"不能作为用户名注册。"; } else { result=eventArgument+"可以注册。"; } //throw new Exception();
}
//定义返回回调方法执行结果的方法
public string GetCallbackResult()
{ return result;
}
//服务器上执行的方法
public void Page_Load(Object sender,EventArgs e)
{ // 获取当前页的ClientScriptManager的引用 ClientScriptManager csm = Page.ClientScript; // 获取回调引用。会在客户端生成WebForm_DoCallback方法,调用它来达到异步调用。这个方式是微软写的方法,会被发送到客户端 //注意这里的"Success"和"Error"两个字符串分别客户端代码中定义的两个javascript函数 //下面的方法最后一个参数的意义:true表示执行异步回调,false表示执行同步回调 String reference = csm.GetCallbackEventReference(this, "args","Success","","Error",false); String callbackScript = "function CallServerMethod(args, context) {\n" +  reference + ";\n }"; // 向当前页面注册javascript脚本代码 csm.RegisterClientScriptBlock(this.GetType(), "CallServerMethod",  callbackScript, true);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<table border="1" cellpadding="0" cellspacing="0" width="400px">
<tr>
<td width="100px">用户名</td><td><input type="text" size="10" maxlength="20" id="txtUserName" οnblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td>
</tr>
<tr>
<td>密码</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td>
</tr>
</table>
</form>
</body>
</html>

上面的页面中我已经添加了足够详尽的注视,不过我还是要说明几点:

(1)

<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %> 

这句表示当前页面实现了ICallbackEventHandler接口,如果采用页面与代码分离的模式,后台cs代码则应是:

public partial class Register : System.Web.UI.Page, ICallbackEventHandler
{
//cs代码
} 

(2)

  1. <input type="text" size="10" maxlength="20" id="txtUserName" onblur="CallServerMethod(txtUserName.value,null)" />

(3)

  1. csm.GetCallbackEventReference(this, "args","Success","","Error",false);

中的"Success"和"Error"分别代表客户端的javascript函数,可以在代码中见到,其中"Success"代表调用服务器端方法成功后要执行的客户端方法名,"Error"代表调用服务器端方法失败时调用的客户端方法名。

该页面在客户端生成的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>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>用户注册</title>
<script language="javascript">
//客户端执行的方法
//下面的方法是接收并处理服务器方法执行的返回结果
function Success(args, context)
{ message.innerText  = args;
}
//下面的方式是当接收服务器方法处理的结果发生异常时调用的方法
function Error(args, context)
{ message.innerText  = '发生了异常';
}
</script>
</head>
<body>
<form name="form1" method="post" action="register.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="/wEPDwUKMjA0MjMxNTU1OGRkIv6UMIqGy3vfPLfPRjEbuTwUrf8=" />
</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>
<script src="/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750" type="text/javascript"></script>
<script type="text/javascript">
<!--
function CallServerMethod(args, context) {
WebForm_DoCallback('__Page',args,Success,"",Error,false); }// -->
</script>
<table border="1" cellpadding="0" cellspacing="0" width="400px">
<tr>
<td width="100px">用户名</td><td><input type="text" size="10" maxlength="20" id="txtUserName" οnblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td>
</tr>
<tr>
<td>密码</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td>
</tr>
</table>
<script type="text/javascript">
<!--
WebForm_InitCallback();// -->
</script>
</form>
</body>
</html> 

在生成的HTML代码中多了几段javascipt教本块,下面分别说明:

(1)第一部分

<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> 

这部分代码是每个asp.net页面发送到客户端都会生成的,用于提交当前表单,其中eventTarget参数表示激发提交事件的控件,eventArgument参数表示发生该事件时的参数信息。

(2)第二部分

<script src="/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750" type="text/javascript"></script>

这部分代码是用来生成一些用于Ajax调用的js脚本。说穿了,asp.net之所以开发起来方便,是因为微软在幕后默默地为我们做了很多工作,回调的本质其实就是Ajax调用。

我们可以将“/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&amp;t=633578466781093750”这部分拷贝到浏览器地址栏中,如下图:

回车之后会弹出一个下载文件对话框,如下图:

将这个页面保存到本地,虽然默认的保存文件的后缀为“.axd”,但它其实是一个文本文件,里面是一些javascript代码,我们可以用记事本打开,在里面我们可以看到“WebForm_DoCallback”这个方法,如下:

在这个axd文件里做了很多幕后工作,所以我们的回调才相对比较简单。

(3)第三部分

<script type="text/javascript">
<!--
function CallServerMethod(args, context) {
WebForm_DoCallback('__Page',args,Success,"",Error,false); }// -->
</script> 

这部分代码是后台生成的,通过获取Page类的ClientScript属性,也就是ClientScriptManager的实例注册到页面的,里面定义了两个javascript函数:CallServerMethod函数和WebForm_DoCallback函数,并且是在CallServerMethod函数中调用WebForm_DoCallback函数。

(4)第四部分

<script type="text/javascript">
<!--
WebForm_InitCallback();// -->
</script> 

这部分代码也是幕后生成的,这个javascript函数也可以在那个axd文件中找到。如下图:

当我们在以除“admin”之外的字符串作为用户名并移开焦点之后,会得到可以注册的提示,如下图:

当我们输入“admin”作为用户名时的结果:

另外,我们将服务器端执行的方法做如下处理,也就是RaiseCallbackEvent(String eventArgument)这个方法,我们在这里抛出一个异常,代码如下:

// 定义在服务器端运行的回调方法.
public void RaiseCallbackEvent(String eventArgument)
{ /* if(eventArgument.ToLower().IndexOf("admin")!=-1) { result=eventArgument+"不能作为用户名注册。"; } else { result=eventArgument+"可以注册。"; } */ throw new Exception();
}

再次运行,无论我们以什么作为用户名,都会得到如下结果:

之所以会出现“发生了异常”这个字符串,是因为我们定义了function Error(args, context)这个javascript函数,并且把它作为调用服务器端方法发生异常时的客户端处理函数,它的处理方式就是显示“发生了异常”这个字符串。

  • 查看图片附件

asp.net夜话之五:Page类和回调技术相关推荐

  1. (转)asp.net夜话之十一:web.config详解

    在开发中经常会遇到这样的情况,在部署程序时为了保密起见并不将源代码随项目一同发布,而我们开发时的环境与部署环境可能不一致(比如数据库不一样),如果在代码中保存这些配置这些信息部署时需要到用户那里更改代 ...

  2. asp.net夜话之九:验证控件

    本篇要讲述的知识点如下: 数据验证介绍 纯客户端脚本验证 asp.net验证控件概述 RequiredFieldValidator控件 CompareValidator控件 RangeValidato ...

  3. 《asp.net夜话》一书视频ASP.NET夜话视频1-21章下载(ASP.NET夜话2009年5月19日更新)

    asp.net夜话教学视频说明 本系列视频是笔者著作<asp.net夜话>的配套视频教程.<asp.net夜话>是根据在csdn论坛里经常问到的问题结合本人多年的网站开发经验而 ...

  4. 《asp.net夜话》一书视频ASP.NET夜话视频1-19章下载(ASP.NET夜话2009年5月15日更新)

    asp.net夜话教学视频说明 本系列视频是笔者著作<asp.net夜话>的配套视频教程.<asp.net夜话>是根据在csdn论坛里经常问到的问题结合本人多年的网站开发经验而 ...

  5. Asp.net夜话之二:asp.net内置对象

    在今天我主要要介绍的有如下知识点: Request Response Server Session Cookie Application <%%>及<%=%>表达式 准确地说, ...

  6. asp.net夜话之八:数据绑定控件

    通过前面的例子我们看到每次我们要显示数据的时候都要通过一个循环来显示满足条件的数据,这是一个比较麻烦的过程,为此微软定义了一系列的控件专门用于显示数据的格式,通过这些控件可以以可视化的方式查看绑定数据 ...

  7. asp.net夜话之三:表单和控件

    在今天我主要要介绍的有如下知识点: HTML表单的提交方式 HTM控件 获取HTML表单内容 乱码问题 SQL注入 服务器端表单 HTML服务器控件 HTML表单的提交方式对于一个普通HTML表单来说 ...

  8. 《asp.net夜话》一书视频ASP.NET夜话视频1-17章下载(ASP.NET夜话2009年5月9日更新)

    asp.net夜话教学视频说明 本 系列视频是笔者著作<asp.net夜话>的配套视频教程.<asp.net夜话>是根据在csdn论坛里经常问到的问题结合本人多年的网站开发经验 ...

  9. 《asp.net夜话》一书视频ASP.NET夜话视频1-14章下载(ASP.NET夜话2009年5月8日更新)

    <asp.net夜话>一书视频1-12章下载(2009年4月13日更新) asp.net夜话教学视频说明 本系列视频是笔者著作<asp.net夜话>的配套视频教程.<as ...

最新文章

  1. postgresql 分组查询第一条数据
  2. 基于Linux(LAMP)平台搭建MYsql数据库(二)
  3. servlet单实例多线程 ---线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。(所有建议不要在servlet中定义成员变
  4. Elasticsearch总体介绍
  5. 架构设计 | 基于消息中间件,图解柔性事务一致性
  6. 新手学linux之-----------memroy.c
  7. .net生成随机字符串
  8. 树莓派3B+,我要跑.NET CORE
  9. 云原生火爆技术人朋友圈,你可别云里雾里了!
  10. 使用GDAL对HDF数据进行校正
  11. ACM程序设计基础(2)题解
  12. 16个车辆信息检测数据集收集汇总(简介及链接)
  13. QQ音乐与网易云音乐评测分析
  14. 怎么批量给多个 PPT 添加自定义的水印?
  15. 高德地图获取坐标距离_高德地图计算两坐标之间距离
  16. 常用元器件的识别与使用(电阻、电容、电感)
  17. matlab流场可视化后处理
  18. bigdata_舆情挖掘项目
  19. Android AsyncTask详解
  20. android 绘画笔迹回放_Android画板 半透明画笔 笔迹叠加效果

热门文章

  1. 【Android 逆向】代码调试器开发 ( 等待进程状态改变 | detach 脱离进程调试 PTRACE_DETACH | 调试中继续运行程序 PTRACE_CONT )
  2. 【Android 插件化】恶意软件判定规则 | 恶意软件的范围定义
  3. 【设计模式】观察者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
  4. 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )
  5. 【组合数学】生成函数 简要介绍 ( 生成函数定义 | 牛顿二项式系数 | 常用的生成函数 | 与常数相关 | 与二项式系数相关 | 与多项式系数相关 )
  6. php 二维数组排序详解: array_multisort
  7. 愤怒的小鸟(爆搜,剪枝)
  8. 今日浅谈循环 for与while
  9. 图论 用广搜搜邻接矩阵
  10. 【转】关于TP3.2 验证码不显示的问题