无论您是系统管理员、设计人员还是开发人员,您的工作都会受到 AJAX 的重大影响。管理员必须确保安全防护足够高,以便应对各种可能的新类型攻击。Intranet 管理员则必须保证任何浏览器上都没有禁用 JavaScript。Web 设计人员需要面对一些新的挑战,因为 AJAX 可实现的功能是以前无法实现或不切实际的。而开发人员则需要熟悉新的 API 和新的全面编程方法。尽管如此,AJAX 对架构师来说究竟有何意义?
AJAX 应用程序具有挑战性,因为它们引入了全新的概念和新的基础。AJAX 模式横跨客户端和服务器环境,因此架构师角色必不可少。对于确定客户端上发生的逻辑和处理与服务器上保留的内容,以及确定客户端和服务器上的哪些数据对象能够交换,清晰的体系结构设想显得举足轻重。
AJAX 派上用场Web 最初用于共享信息的静态页面。短短几年之后,它发展成一种更加动态的媒介。如今,Web 由交互性极强的页面所组成;然而,它仍非常依赖于以整页转换为基础的模式。用户与页面进行的交互越多,必须创建及返回用户浏览器的页面就越频繁。以此方式使用交互式页面所引起的负面结果有很多,例如,减缓页面刷新和闪烁。这是一种不完善的解决方案,经常会导致糟糕的用户体验。AJAX 使得此模式中发生了非常令人渴望的变化,它允许更简洁的 Web 应用程序,并为生成新的应用程序奠定了基础。AJAX 以最终用户为核心。使用 AJAX 功能,Web 应用程序的交互性变得更强,响应更及时,更快速且更友好。交互和响应(操作缓慢时的反馈)都更妥善地得到了处理,便于用户对页面进行更多操作。总之,用户获得了更好的体验。
使用 AJAX,更多代码会在浏览器上执行,这要求将更多脚本指定给客户端页面。但是,这留下了一些大问题有待解决。何种脚本?谁将编写此脚本?此脚本遵循哪些体系结构原则和模式?
在本专栏中,我将从体系结构角度介绍 AJAX,帮助您、开发人员和架构师做出明智选择。同时,我将为设计人员和管理员提供足够的上下文信息,让他们的工作变得更加轻松。这是一个相当大的主题,因此我把它分为两部分来讲。请不要错过下个月的专栏,届时我将继续这一讨论。
AJAX 体系结构如果您要考虑 AJAX,请看图 1,该图说明了这一步的体系结构含义。传统的 Web 应用程序需要在服务器上执行所有操作,应用程序只是偶尔会发出脚本代码,在客户端上运行任务,如处理数据验证。
图 1 传统 Web 模式与 AJAX 模式
AJAX 应用程序使用负责对 Web 服务器发出调用的客户端框架。AJAX 服务器端框架则负责请求数据馈送并将其返回客户端。这通常是 JavaScript Object Notation (JSON) 数据流,但也可使用其他格式,如 XML、RSS 和 CSV。
客户端接收数据馈送,并使用 JavaScript 更新 UI。该服务器通过返回以指定格式编码的原始数据,对请求做出响应。带宽消耗会降至最低,应用程序速度会提高(因为完成请求所用时间较少),而且 UI 更新也能在无可见回发的情况下生效。但是,在解决众多问题的同时,客户端上操作的增加也带来了新的问题,如新的编码实践、新的安全隐患、可访问性问题等。
什么是 AJAX 框架?为了让 AJAX 在网页中有效,必须满足一些条件。首先,您的浏览器必须能够支持 JavaScript,此外还需要全时 Internet 连接,因为 AJAX 应用程序无法脱机工作。当所有请求都在页面级生成时,由于它们带有非 AJAX 应用程序,浏览器可通过这些页面的缓存集来提供导航,因此能够脱机进行。而在与使用 AJAX 应用程序的浏览器合作的基于脚本的框架中,同样的行为则必须进行显式编码。这一领域有很多新东西涌现,并且一些新的帮助工具正在开发中。让 AJAX 应用程序脱机工作,这对众多的软件供应商来说是一个挑战。
任何真正的 AJAX 应用程序都需要一个特定框架。通常,首选框架会在单独的客户端和服务器部分明确说明。
AJAX 页面中有两种主要类型的脚本代码:首选框架的系统级代码和实现页面预期行为的用户级代码。系统级代码提供用于向 Web 服务器发送异步请求并处理响应的引擎。用户级代码实质上使用文档对象模型 (DOM) 脚本更新页面的 UI。这里便遇到我先前提及的其中一个问题。由谁编写哪段?为讨论这点,我将重点介绍一种特别的 AJAX 框架 — ASP.NET AJAX Extensions。
ASP.NET AJAX Extensions
ASP.NET AJAX Extensions,即 ASP.NET 2.0 的一个扩展,为新网站和现有网站提供 AJAX 功能。ASP.NET AJAX 有两种编程模型:部分呈现和远程服务。它们应用程序中采用两个全然不同的体系结构,因而具有不同的优缺点。(您可能已经领会到,AJAX 中的多数功能都涉及权衡问题。)
简而言之,部分呈现允许您维护与传统 ASP.NET 2.0 应用程序类似的体系结构。它只提供了一套新的服务器端工具,您可以用其来实现无闪烁页面更新。
而远程服务则涉及由脚本相对较多的 AJAX 前端所调用的面向服务的后端。几乎所有的基本应用程序进程(包括身份验证、数据分页和分类)都必须重新设计。服务器端代码必须分解为特定于应用程序的服务,并且必须选择一种格式(如 JSON)用于数据交换。最后,必须安排一个前端,特别注意将您的编码限制为 UI 级任务,并保持多数业务逻辑避开客户端。
远程服务方法提供了更完整的 AJAX 体验,而部分呈现对现有应用程序中已有的功能进行渐进式增强,从而提供了更流畅的转换。
这两种方法在某种程度上可以结合使用。例如,您可以保留同一传统 Web 体系结构,在各处添加一些无闪烁更新,然后花时间以面向服务的方式重构某些关键功能。ASP.NET AJAX Extensions 提供了几个关键因素,但创建重要 AJAX 应用程序的主要工作还是取决于用户。图 2 显示了 ASP.NET AJAX Extensions 框架的客户端和服务器组件。
部分呈现一览
Jeff Prosise 在 2007 年 6 月的“超酷代码”专栏 (msdn.microsoft.com/msdnmag/issues/07/06/WickedCode) 提供了 ASP.NET AJAX 部分呈现的精彩讨论。从体系结构观点看,部分呈现不添加任何新内容。它只是使用某些 AJAX 功能来增强旧应用程序的一种绝佳方法 — 其中最重要的是无闪烁页面更新。
部分呈现不需要学习许多新技能,它对现有代码的影响也非常有限。对于可能会因采用 AJAX 而受影响的应用程序方面,例如安全性和可访问性,部分呈现还提供了简明的回调机制。一般来说,如果在升级过程中您无法将现有代码的某些部分转变成 AJAX,那么可以让这些代码块保持原样,然后继续。
部分呈现请求通常称为 AJAX 回发。该术语十分恰当,可准确表示实际发生的情况。AJAX 回发与传统 ASP.NET 回发相似,不同的是,前者由客户端 ASP.NET虽然表面上类似纯 AJAX 远程调用,但 AJAX 回发看上去好像是对 ASP.NET 运行时组件的普通回发请求 JAX 库中定义的一段脚本代码来执行。虽然表面上类似纯 AJAX 远程调用,但 AJAX 回发看上去好像是对 ASP.NET 运行时组件的普通回发请求。到达服务器之后,该请求便会经历典型回发请求的生命周期,并引发诸如 Init、Load 和 PreRender 等事件。在服务器上,AJAX 回发与传统 ASP.NET 回发的区别仅在于用来呈现最终标记的算法不同而已。不同的算法是提高性能和没有页面闪烁的关键。但是,该应用程序模型仍与 ASP.NET 中的相同。
除了呈现阶段的实现之外,AJAX 回发和 ASP.NET 回发中的所有情况均相似。AJAX 回发仍会触发通知事件(如 Load 和 PreRender),并且它仍会处理视图状态信息,并引发状态更改和回发事件(如 TextChanged 和 Click)。
ASP.NET AJAX 页面必须包括 ScriptManager 控件的一个实例。此控件是 ASP.NET AJAX 页面真正的控制中心。它负责将页面链接到任何所需的框架脚本文件,并在检测到发生 AJAX 回发时协调部分呈现。ScriptManager 控件会检查请求中的 HTTP 头,以确定该请求是否为 AJAX 回发。以下是 MicrosoftAjaxWebForms.js 文件的摘录,该文件可在触发 AJAX 请求之前设置 HTTP 头:
request.get_headers()['X-MicrosoftAjax'] ='Delta=true';
要了解使得 AJAX 回发呈现阶段不同的因素,请仔细查看以下 System.Web.Extensions 程序集的摘录:
protected override void OnPreRender(EventArgse){
base.OnPreRender(e);
if (IsInAsyncPostBack)
PageRequestManager.OnPreRender();
}
特别是,上述代码段均来自 ScriptManager 控件。如您所见,脚本管理器会注册一个预呈现事件处理程序。触发时,处理程序会检测它是否属于 AJAX 回发,如果是,则从 PageRequestManager 内部类调用一个方法。PageRequestManager 类的 OnPreRender 方法会执行以下操作:
internal void OnPreRender()
{
_owner.SetRenderMethodDelegate(newRenderMethod(this.RenderPageCallback));
}
该方法会设置特定的子例程来呈现当前请求的标记,非常简单。SetRenderMethodDelegate 是在 System.Web.UI.Control 类中定义的方法。此方法不作公用,它基本上分配一个事件处理程序,页面或控件使用该处理程序来呈现其内容。在先前的代码段中,该方法会在当前页面被调用,并以覆盖当前请求的整个呈现过程而结束。因而,负责 AJAX 回发标记的真正代码在 RenderPageCallback 方法(PageRequestManager 类中的内部方法)中定义。(有关 ScriptManager 控件的更多信息,请参阅本期《MSDN®
杂志》中 Ben Rush 的文章。)
基本上,AJAX 回发的已修改呈现过程会遍历页面上所有涉及回发并累积每一个标记的可更新面板。所有标记块会与隐藏字段和任何错误信息一起打包到响应流,并传递给客户端。现在让我们深入剖析示例页面上下文中的典型部分呈现调用。
剖析 AJAX 回发
若要使 ASP.NET 页面成为部分呈现的页面,首先必须向页面添加一个脚本管理器,然后通过使用 UpdatePanel 控件进行封装,从而定义可独立更新的区域。例如:
< ?xml:namespace prefix = asp />
< asp:ScriptManager runat="server">< /asp:ScriptManager>
< asp:UpdatePanel id=UpdatePanel1 runat="server">
< CONTENTTEMPLATE>
< %-- Markup of the region goes here --%>
< /CONTENTTEMPLATE>
< /asp:UpdatePanel>
UpdatePanel 控件不会以任何方式改变为该区域生成的可见标记,而只是向原始标记添加一个外围
标记:
< DIV id=UpdatePanel1>
< %-- Markup of the region goes here --%>
< /DIV>
什么会触发 AJAX 回发?如何进行管理?由谁管理?每当脚本管理器在页面上检测到一个或多个 UpdatePanel 控件时,它会发出如下的脚本程序块:
< SCRIPT type=text/javascript>
Sys.WebForms.PageRequestManager._initialize('ScriptManager1',
document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(
['tUpdatePanel1','tUpdatePanel2'], [], [], 90);
< /SCRIPT>
_initialize 方法是客户端 PageRequestManager 对象上的静态方法(参见 MicrosoftAjaxWebForms.js)。它会创建一个 PageRequestManager 类的全局实例,并将其初始化。该类充当一个单例,并且是后来可通过 getInstance 方法来检索的唯一可用实例。上述代码段的第二个语句向客户端框架注册了一组 UpdatePanel 控件。每个服务器端 UpdatePanel 控件都通过其 ID 进行引用。
此处发生的关键操作在 _initialize 方法内。如上所述,在创建该类的单个实例后,该代码会将其初始化。此时,还为 DOM 表单对象的提交事件注册了一个处理程序。这意味着,每当页面提交表单时,AJAX 脚本会介入并使用 XMLHttpRequest 发出请求,而不是让请求经历普通的浏览器回发。最初的表单字段集保持不变,并且为服务器端脚本管理器方便起见,会附加一些额外信息。因此,AJAX 回发上载的信息要比常规 ASP.NET 回发多一点。
视图状态以及任何其他隐藏字段随请求一起执行并上载到服务器。返回途中,会一同下载更新的视图状态与新的隐藏字段及可能更短的标记(如果有)。特别是,该响应只包括回发期间修改的可更新区域的标记。该列表包括触发回发的 UpdatePanel 控件(和所有嵌套面板)、页面中 UpdateMode 属性设置为 Always 的任何其他 UpdatePanel 控件,以及以编程方式刷新的所有 UpdatePanel 控件。以下代码是如何根据运行时情况以编程方式刷新面板的一个示例:
UpdatePanel1.Update()
您可以使用各种工具来监视入站和出站的 HTTP 数据包。此专栏中,我使用的是 Nikhil Kothari 的 Web Development Helper 工具,您可以从 projects.nikhilk.net/Projects/WebDevHelper.aspx 免费下载。
AJAX 回发的响应是一个文本流,可将其视为具有大小、类型、ID 和内容列的记录表格。每条记录都称为一个 delta 节点。
delta 节点标识了 AJAX 回发期间可能出现的更改。并非所有的响应都有相同的 delta 节点集,这取决于请求的服务器生命周期内发生的事件及原始标记的结构。
目前支持的所有 delta 节点。负责处理 delta 表格的 JavaScript 代码位于 MicrosoftAjaxWebForms.js 文件中。