**先讲一下异步调用的背景
**

谈到异步调用,大家第一反应就是 ajax。这没有错,毕竟这是一个 ajax 的时代,ajax 以它最令人兴奋的客户端体验被大家普遍使用到 Web 开发中来。然而,我们知道,ajax 的本质仍是 Javascript操作,那它就逃脱不了 Javascript 的最大隐患之一:安全。跨站脚本攻击的伎俩是不需要多少技术的,一个低级的初学者就可以利用最基本的脚本代码来访问我们精心设计的服务器调用,让人防不胜防。不过,我并不是要埋没由高效的 Javascript 带来的令人兴奋的 Ajax 技术的优点。

既然 Ajax 有其不得不用的益处,我们却又不得不面对其脆弱的安全性能,那有没有一个折中的办法,可以同时很好地解决这两个问题呢?在 asp.net 2.0中,asp.net 的创造者在客户端调用方面给我们带来了福音。客户端回调是ASP.NET中一个非常好的特性,它采用了 ajax 的方式异步调用保证性能,且能更好地保证安全。可以说回调是 ASP.NET 2.0 对AJAX的原生支持吧。熟悉 Page 类的朋友知道, Page 类有一个属性叫做 IsCallback,它可以用来检测当前页面执行是不是由回调引发;从 asp.net 的作者把这个属性内置于 Page 类中就可以看出微软对回调寄予的厚望了。

下面,让我们来看一下回调的用法。

ASP.NET 2.0 提供了一个新的接口ICallBackEventHandler,为了实现客户端回调Page类必须来实现这个接口。它有两种方法:
RaiseCallbackEvent:处理以控件为目标的回调事件,这个方法将在客户端脚本调用服务器代码时来执行;
GetCallbackResult:返回以控件为目标的回调事件的结果,结果通常被RaiseCallBackEvent 和存储在一些类中成员变量返回。 
 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public partial class _Default : System.Web.UI.Page, ICallbackEventHandler 
{ 
    string callbackResult; 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     // 稍后讨论这里 
    } 

    public void RaiseCallbackEvent(string eventArgument)
    { // 服务器端操作  

        callbackResult = “Hello World Form CallBack!”;
}    

    public string GetCallbackResult()
    {
        return callbackResult;
    }
}

上面的代码简单的在RaiseCallbackEvent中执行了一个操作并且把它保存到了结果callbackresult变量中,这个结果将被GetCallbackResult方法返回。下一步在客户端增加一些JavaScript脚本来触发这个回调。 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<script type="text/javascript"> 

function GetMessageFromServer() { 
//在这里我们的脚本定义名称为 UseCallBack 的函数,因为它将会动态生成
UseCallBack();   
} 
function JSCallback(args, context) { 
// 当回调结束后将执行这里的方法,把从服务器端获取的值进行客户端操作 
document.forms[0].TextBox1.value = args;
} 

</script>

 “GetMessageFromServer”将调用UseCallback来触发服务器端代码,这个“UseCallback”方法是动态的方法,它将从服务器端后台代码处生成。“JSCallback”方法将在服务器端代码调用完成以后在回调执行。在这里我们是返回了一个字符串。在前台,我们放置一个 HTML 文本框来显示此值,并添加一个 HTML 按扭用于激法回调操作:

现在开始最后也是最重要的步骤,负责从Javascript发送XmHttp来调用服务器端的方法,通常的以下代码将放到Page_Load事件中执行
protected void Page_Load(object sender, EventArgs e)
{
    // 获得客户端回调方法JSCallback的引用
    string cbref = Page.ClientScript.GetCallbackEventReference(this, “args”, “JSCallback”, “context”);
    // 生成一个JavaScript脚本来触发回调
    string cbScr = string.Format(“function UseCallBack(args, context) {{ {0}; }} “, cbref);
    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), “UseCallBack”, cbScr, true);
}

上面的C#代码在运行时,将在页面生成如下的JavaScript代码:

function UseCallBack(args, context)
{
    WebForm_DoCallback(’__Page’,args,JSCallback,context,null,false);
}

这样,在页面呈现之后,我们在客户端点击“获取回调结果”的按扭,就会将从服务器上执行完毕返回的结果填充到文本框中。
而当我们仔细查看呈现之后的代码可以发现,HTML 逻辑中除自动添加上了 ViewState 隐藏域和上面的 UserCallBack 方法之外,还有一段脚本引用代码,其引用格式如下:

其中的查询值是随机生成的,可能与在运行时各有不同。这是一个脚本引用的 URL,我们可以复制其URL,并在浏览器的地址栏中输入它,然后下载到本地,用记事本打开,可以发现其中有很多 Javascript 代码。仔细研究它的代码发现,它其实就是 asp.net 自动生成为了帮助使用回调的脚本资源,而深入研究这些脚本可以看到,正是它们帮助回调页面完成了一系列的复杂脚本,而使用的技术依然是 XmlHttpRequest 异步请求。
也就是说在回调机制中,实际上页面也正是使用了与 Ajax 同样的 XmlHttp 对象回调了服务器上的页面代码,并最终从服务器端返回回调结果到客户端方法。服务器端执行完成之后框架将通知JavaScript方法告知服务器已经执行完毕,例子中将调用function JSCallback(args, context)。当回调发生时,在 Page_Load 事件中添加断点可以发现,此时 Page.IsCallBack 属性为 true,而 Page.IsCallBack 同时也为 true了。

下面,我们深入一点认识回调机制。

客户端回调的一个特点,当服务器端回调时,Page是装载到内存并且执行了Page生命周期中通常的方法,像 Page_Init Page_Load等。Ajax 正相反,因为Ajax的页面不会装载到内存中,所以也就不能访问 ViewState,不能访问服务器控制,不存在 HttpContext,甚至连 Session 也不能访问。回调居于前两者之中,它执行了页面周期的大部分事件关键,而像Pre_RenderRender 等则不会触发,其实这也是符合逻辑的,因为我们并不想让页面刷新。可以使用下面的图来描述Page的生命周期。

通常的 Asp.net 访问请求的生命周期:                                            回调时的页面生命周期:
                 

可以注意到回调和回传触发的页面生命周期不完全相同,回传会触发完整的页面生命周期,回调则只会触发页面的前期生命周期过程,比如 Init 和 Load,而不会触发状态加载、呈现内容等过程。进一步使用 Session 来做测试能够惊讶地发现,回调能够使用 Session,不论是读取或者写入!是啊,正因为回调使用了与普通页面回传的相似的前期机制(都驱动发生 Load State 与 Load 事件等),它可以拥有 HttpContext,以及一切与此关联的对象。这也是回调吸引我的原因之一。

**Asp.net 服务器技术浅议,回调与 Ajax 的比较
**

开始,乃至今日,很多人难以理解 Asp.net 的服务器机制,用怀疑的眼光来看待这一切,用无法接受的态度来看待 Asp.net 的服务器控件。这些人最终不能接受将 Asp.net 作为自己的日常技术,因为他们不了解 Asp.net 服务器脚本托管编译运行的原理与本质。甚至不少人将 Asp.net 的运行与 Asp 作概念上的比较,实在是不应该。
asp.net 之所以成为 asp.net,正因为它有了服务器托管平台,它充分发挥服务器的主动性,让服务器——说到底也就是开发人员来主宰这个互联网。拿 asp.net 服务器控件作为 asp.net 服务器技术的代表来说明问题。我们稍后再议它的效率,单就它在 Asp.net 2.0 中应不应该被广泛应用以及如何广泛应用的问题,我们来讨论一下。
我们都知道, asp.net 的逻辑代码中,极为深入地体现着面向对象的思想,无论是托管代码还是 asp.net 本身的概念。它可以创造出各种形形色色的功能与界面,而且这一切都由开发人员使用它们的 asp.net 代码来完成并使用托管代码来管理,可视化的编码与调试、便捷的调试与测试、可控的缓存、庞大的可用类库、跨平台的对象存储与管理——这一切,你只需要一台能够运行 asp.net 网站的服务器——和一款连智能设备也能提供的浏览器。
可以这么说,之所以要使用 asp.net,就是因为要使用它的服务器技术;使用 asp.net 的服务器技术,就应该使用服务器组件,包括服务器控件、用户控件及自定义控件(使用 C# 代码直接构建并呈现)。asp.net 服务器开发技术极大地体现了开发人员的主动性,极大地发挥开发人员的聪明才智。在此基础上,由开发人员为用户分配足够发挥的接口——客户端页面界面及脚本程序,而客户端的处理亦由 asp.net 服务器组件负责处理,这就是 asp.net 的精神。
近年来,我们看到在面向 Internet 普通用户的应用中,动态网站技术越来越发展状大, Web 2.0 已经来临。于是 asp.net 在效率方面的问题被逐渐认识到。不得不面对这样的事实: asp.net 在普通互联网应用是不受推荐的。于是就有不少人认为 asp.net 的服务器技术是一种令人不齿的缺点而诟病之。其实不然。我们讲 asp.net 开发的是 Web 应用程序,而不是普通的 WebPage,如果只是网页引擎,那 asp.net 的得分自然是大打折扣了;但请注意,它不是网页引擎,它是应用程序引擎。这本质就变了。 asp.net 可以用来构建高速的企业网站、管理协调以及其他任务,这也正是服务器技术发挥特色的领域。如果你要使用 asp.net 来构建一个网页引擎,那它也只应该被应用在后台管理平台中,而不是前台。

如果我们必须——不得不将 asp.net 应用于前台呢?
Web 2.0 给人们带来了最令人兴奋的实时互动,发挥每一个人的思想;而发展受到种种因素的另一方面却远没有软件思想发展来得快:基础设施发展滞后让我们新的软件思想与前端技术的应用大打折扣。人总是那么的聪明,总能想到种种令人拍案叫绝的方案来解决这些矛盾。基于这一点, Ajax 在 Web 2.0 应用中一夜春风、如鱼得水。习惯上,我们把从客户端的 JavaScript 通过 XmlHttp 的方式调用服务器端的代码,叫 Ajax 。技术层面上说 Ajax 是一种客户端脚本技术,它与 Asp.net 无关;可它注定与 Asp.net 有着不解之缘,似乎自从 asp.net 出现的那一天起就注定了,因为太多时候,我们必须,且不得不将 asp.net 应用于 Internet 前台。

在ASP.NET 1.1 的时代,想使用Ajax是相当繁重的一项工作。首先你需要拷贝Ajax.dll到站点的Bin目录中,然后在 Web.Config 中设置相关的 HttpHandler,最后在页面中使用代码来注册组件——有些类似于今日的 AjaxPro。在 ASP.NET 2.0中,微软适时地内置了原生的 Ajax 支持组件:回调技术。
回调技术被整合到 asp.net 的服务器技术中,使开发人员及客户端用户感觉一切是这么的自然。是啊,回调技术已经融入到 asp.net 中来,成为其中一员,因此我们可以使用它。这意义是重大的。它为我们提高服务器组件的效率带来了福音。纵观回调技术,可以发现它有如下优点:
回调是 ASP.NET 2.0 中的成员,是一种标准(Standard)的使用方式。
回调可以访问页面的ViewState和Form控件。
一切与 ASP.NET 集成,无需其他思考,增强了安全与正确的保证。

至于 ajax,我想讨论它的文字已经够多了,多得无需我再多写哪怕一个字。