用ASP.NET 2.0实现AJAX风格的Web开发

简介:
提要 在过去的几个月中,基于AJAX技术开发高度交互的Web应用程序的设计模式迅速流行开来。现在,具有高度可配置性的Web应用程序,例如Google Maps和A9,都在综合利用这些技术来创造丰富的客户端用户体验。其实,结合AJAX技术进行Web开发并非最近的研究成果,只不过这些技术一直以来不断得到持续更新和改进。

本文中我有三个目的。首先,我想提供一个AJAX风格应用程序的高级概述。其次,我想详细地描述ASP.NET 2.0的异步回调机制。最后,我想对构建AJAX风格应用程序的工具和框架的未来改进作一下展望。

归纳来看,AJAX风格的Web应用程序展示了下列特征:

· 到Web服务器的异步请求-在用户等待来自于Web服务器的响应时,浏览器用户接口不会被堵塞,而是可以继续响应用户的交互。

· 高度依赖于用JavaScript编写的基于浏览器的逻辑-W3C DOM的最新改进和标准化为实现动态的客户端UI更新提供了支持。

· 在浏览器和Web服务器之间的基于XML数据的交换-XMLHttp对象使得与Web服务器进行通讯而不需要重载页面成为可能。

一个AJAX应用程序和传统型Web应用程序之间的最大差别是,每次用户交互不会导致每一个HTTP请求都被发送到Web服务器;而是,用JavaScript实现的基于浏览器的逻辑掌握着控制权,之后再由该控制决定是局部处理请求还是向服务器作异步调用。一旦到服务器的异步调用结束,客户端逻辑立即适当更新UI的相关部分。这种方式具有下列优点:

· 用户体验更为丰富。例如,当一个Google地图用户沿一个方向拖动地图时,系统就会在后台向服务器发出一个异步请求,结果是他能够在超出屏幕边界后继续拖动。这样以来,当用户进一步拖动地图时,新的图像已经可用了。这导致一种响应更快的感觉。

· 既然跨越基于XMLHttp的到服务器的调用状态并没有丢失,那么,AJAX应用程序就可以避免每次都重新生成UI界面。

· 更多的逻辑位于浏览器端,从而减少了到Web服务器的来回请求的数量,进而全面改进系统的潜力。

尽管存在这么多的优点,然而AJAX风格的应用程序还是存在一些不足之处。例如,AJAX风格应用程序的开发是比较困难的,因为缺乏相应的框架(一组类似于Windows MFC工具包的UI类)和IDE(调试,可视化设计,等等)支持。另外,基于AJAX进行开发要求一个人必须至少掌握两种语言(DHTML和JavaScript)。而且,AJAX风格应用程序的编码需要更长的时间,因为它需要另外的测试以使其支持多浏览器版本和类型。最后,由于基于JavaScript的源码为终端用户可存取,所以开发过程中的安全分析也变得非常重要。

幸好,例如Atlas,AJAX.NET和Google Maps API等工具的出现为将来构建AJAX风格的应用程序提供了更好的支持。接下来,我们将讨论一下,对于构建AJAX风格应用程序的支持技术的发展历程以及我们能够从最新发布的工具集Atlas得到怎样的期望。

让我们首先讨论XMLHttp对象。这个对象最初为微软所引入,以后在其它平台(包括Mozilla和苹果公司的Safari浏览器)上也得到实现。XMLHttp支持到Web服务器的异步请求,这样可以允许客户端基于JavaScript逻辑调用Web服务器而不需要重载整个页面。

换句话说,在后台与Web服务器的交互而不引起整个页面重载是完全有可能的。

至于XMLHttp对象的使用则相当直接。为简单起见,让我们仅考虑IE特定的语法。其实,XMLHttp在其它浏览器上的实现语法与这里的讨论也很类似。

request = new ActiveXObject("Microsoft.XMLHTTP");
if (request){ request.onreadystatechange = CallbackHandler;
request.open("GET", URL, true);
request.send();
}
function CallbackHandler(){
if ((request.readyState == 4) && (request.status == 200){
string response = request.responseXML;
//更新UI的相关部分
}
}

在上面的代码片断中,第一步实现实例化Microsoft.XMLHttp类。第二步设置我们刚刚创建的XMLHttp实例的属性,其中包括当XMLHttp请求完成时将得到控制的回调函数的地址。因为我们在向服务器作异步调用(通过把open方法的第三个参数设置为true来实现),所以我们需要回调函数的地址。在回调函数实现过程中,我们作额外的检查以确保完成请求。

你从上面的示例代码中可以看出,以独立方式使用XMLHttp对象是相当简单的。然而,把XMLHttp集成到HttpPage生命周期的其它部分中是比较困难的-例如,如何确保服务器端的方法调用能够存取页面中其它控件的状态呢?为了正确初始化这些控件的状态,服务器端的回调处理需要经历一个与回调过程类似的HttpPage生命周期。直接使用XMLHttp对象的其它挑战是,作为开发者,我们需要考虑不同的浏览器类型。幸好,ASP.NET 2.0提供了一个可重用的模式-它能够使得存取回调功能非常容易。注意,随同ASP.NET 2.0一同发行了若干控件,包括GridView,TreeView等,都综合利用了回调机制。

让我们先看一下服务器端实现原理。首先,在服务器端要定义一个新的接口IcallBackEventHandler。任何ASPX页面(或打算支持客户端回调的控件)都需要实现这个ICallBackEventHandler接口。ICallBackEventHandler接口定义了一个称为RaiseCallbackEvent的方法。这个方法使用一个字符串类型的参数并且返回一个字符串。
在客户端,为了初始化回调功能,需要调用一个特殊的JavaScript函数。你可以通过调用ClientScriptManager.GetCallbackEventReference来获得一个到这个特殊的JavaScript函数的引用。到GetCallbackEventReference的调用将产生一个回调引用。当调用此回调函数时,你只需要传递一个字符串类型的参数。这是与服务器端的RaiseCallbackEvent签名一致的。这就是你在客户端建立回调机制所需做的一切。其它的把客户端回调函数钩(hook up)到服务器端的IcallBackEventHandler接口的RaiseCallbackEvent方法的实现则是由框架来完成的。前面提到的初始化回调机制的特殊JavaScript函数使用了另外两个参数(__CALLBACKPARAM和__CALLBACKID)作为回馈数据,它们分别代表传递到调用者的字符串参数和控件的ID。在服务器端,ASP.NET检测其它两个参数的存在并且会把请求路由到适当的控件,这将导致调用目标控件上的RaiseCallbackEvent方法。为了解决前面提到的页面上的控件的初始化问题,ASP.NET运行时刻在服务一次回调时提供了一个简化版本的HttpPage生命周期。这一周期包括浏览页面初始化的某个具体阶段,观察状态加载,页面加载和回调函数事件处理等。一旦回调函数事件被控件所处理,HttpPage生命周期的其它阶段就会被跳过。

为了帮助更好地理解ASP.NET 2.0的回调机制,发行包中包括了一个简单的进度条控件,它依靠回调来决定服务器确定的一项任务的状态。下面的列表1显示了该ProgressBar控件的代码。为了支持客户端回调函数,这个控件实现了ICallbackEventHandler接口。为了演示之目的,RaiseCallbackEvent方法实现简单地查找存储在会话中的一个计数器,每次给计数器加1,并且把新值返回到客户端。最后,列表2显示了负责初始化该回调函数的JavaScript代码。它使用了this.Page.ClientScript.GetCallbackEventReference来获得一个到需要初始化回调的函数的安全引用。

列表1:ProgressBar.cs

public class ProgressBar : System.Web.UI.Control, System.Web.UI.ICallbackEventHandler{
private int PercentCompleted{
get
{
if System.Web.HttpContext.Current.Session["PercentComplete"] == null) {
System.Web.HttpContext.Current.Session["PercentComplete"] = 1;
} 
else {
System.Web.HttpContext.Current.Session["PercentComplete"] =(int)System.Web.HttpContext.Current.Session["PercentComplete"] + 1;
}
return (int)System.Web.HttpContext.Current.Session["PercentComplete"];
}
set
{
System.Web.HttpContext.Current.Session["PercentComplete"] = 1;
}
}
public string RaiseCallbackEvent(string eventArguments) {
int percent = this.PercentCompleted;
if (percent > 100)
{
this.PercentCompleted = 1;
return "completed";
}
else
{
return percent.ToString() + "%";
}
}
protected override void OnPreRender(EventArgs e) {
this.Page.ClientScript.RegisterClientScriptBlock(typeof(ProgressBar),
"ProgressBar", this.GetClientSideScript(), true);
base.OnPreRender(e);
}
protected override void Render(HtmlTextWriter writer) {
System.Text.StringBuilder sb = new StringBuilder();
sb.Append(@"<table id=""ProgressBarContainer"" bgcolor=""LightSteelBlue""
border=""0"" width=""400"" style=""DISPLAY:none; POSITION: absolute;
Z-INDEX: 10"">");
sb.Append(@"<tr><td colspan=""3"" style=""padding:3px 2px 2px 10px"">");
sb.Append(@"<font face=""Verdana, Arial, Helvetica, sans-serif"" size=""2"">");
sb.Append(@"<span id=""ProgressBarLabel"">Uploading...</span>");
sb.Append(@"</font></td></tr><tr><td>");
sb.Append(@"<font size=""1""> </font></td><td bgcolor=""#999999""
width=""100%"">");
sb.Append(@"<table id=""ProgressBar"" border=""0"" width=""0""
cellspacing=""0"">");
sb.Append(@"<tr><td style=""background-image:url(progressbar.gif)"">
<font size=""1""> </font></td>");
sb.Append(@"</tr></table></td>");
sb.Append(@"<td><font size=""1""> </font></td></tr>");
sb.Append(@"<tr height=""5px""><td colspan=""3""></td></tr>");
sb.Append(@"</table>");
writer.Write(sb.ToString());
base.Render(writer);
}
private string GetClientSideScript() {
System.Reflection.Assembly dll =
System.Reflection.Assembly.GetExecutingAssembly();
StreamReader reader;
reader = new StreamReader(dll.GetManifestResourceStream("ProgressBar.txt"));
StringBuilder js = new StringBuilder(reader.ReadToEnd());
string fp = this.Page.ClientScript.GetCallbackEventReference(this, "", "UpdateProgressBar", "");
js.Replace("##InitiateCallBack##", fp);
reader.Close();
return js.ToString();
}
}

列表2:ProgressBar.js

<script language="javascript">
var isCompleted=false;
//这个函数初始化到服务器端的回调
function DrawProgressBar(){
##InitiateCallBack##;
if (!isCompleted) {
window.setTimeout('DrawProgressBar()',200);
}
else
{
isCompleted=false;
document.getElementById("ProgressBarContainer").style.display = 'none';
}
}
//当thecallback完成时,下列函数被调用
function UpdateProgressBar(percent){
if (percent == 'completed'){
isCompleted=true;
}
else{
document.getElementById("ProgressBar").width = percent;
}
}
通过使用在ASP.NET 2.0提供的客户端回调函数,实现进度条控件是比较直接的,因为在控件和客户端之间传递的数据仅是一个简单的字符串。然而,一旦我们把其它数据类型也添加到其中,我们就遇到在JavaScript和.NET类型系统之间不匹配的问题。遗憾的是,ASP.NET 2.0中的回调函数实现对此并无多大帮助。任何想使用多种数据类型(简单类型和复杂类型)的应用程序,都要实现一种自己的定制模式。

幸好,这种限制能够通过使用一个AJAX.NET开源库来加以克服,AJAX.NET实现了一种基于代理的方式来调用服务器端函数。AJAX.NET定义了一种称为AJAXMethod的定制属性。当一个服务器端方法用AJAXMethod加以修饰时,一个基于JavaScript的客户端代理将被HttpHandler(它是AJAX.NET库的一部分)自动生成。不同于ASP.NET 2.0,它支持单个参数的字符串类型以便用于回调实现。AJAX.NET支持整数,字符串,双精度数,DateTime,DataSet等多种类型。

Bertrand Le Roy建议使用AJAX.NET来处理JavaScript和.NET类型系统之间的差别。他创建了一种称为EcmaScriptObject的服务器端控件-它基于.NET技术重新创建了JavaScript类型系统。其想法是,用.NET重新生成一种客户端对象图。当转换发生在服务器端时,这种方法显得更有意义。

即使我们有了一种类型安全的方法来调用回调函数,但是,我们还面临其它的挑战。JavaScript担当起了把AJAX应用程序的各个部分组合到一起的"胶水"的作用。当然,相应地,对JavaScript的依赖性也进一步增加。遗憾的是,尽管JavaScript是一种强有力且通用的语言,但是它并没有实现面向对象的原则。这意味着,要实现代码重用可能更为困难。当然,可以使用一些技巧来使JavaScript看上去更象传统的面向对象语言。不过即使如此,要实现托管语言中的例如事件和代理等特征仍然相当困难。 

其它困难还包括:缺乏一个可重用框架来进一步提高JavaScript的开发效率。如果有一种基于JavaScript的能够隐蔽不同执行环境区别的UI框架或许更好些。另外,如果能够创建一组类,它们可以用一种安全的方式(相对于手工编码SOAP包并使用XMLHttp来传递它们)来调用Web服务,也会相当不错。

最近来自微软的Atlas工程许诺要重点解决这类问题。这是一种极大程度地简化AJAX风格开发的伟大尝试。Atlas提供了一种新的JavaScript框架(注意,下面是基于微软的一次初步宣布,以后有可能发生改变)-UI开发工具包。这其中包括:支持诸如拖放和数据绑定等特征的常用控件;调用Web服务的SOAP栈;隐蔽浏览器差别的浏览器兼容层;包括例如本地缓冲等内容的客户端构建模块。另外,ASP.NET团队还计划为ASP.NET开发其它构建模块,例如配置管理,成员管理等,以便把它们用作Web服务端点,从而实现可以直接从JavaScript中对Web服务进行存取-例如可以容易地从客户端存取个人信息。最后,Atlas工程还计划扩展JavaScript语法以便包括接口、生命周期管理和multicast事件。

据说,接下来的几个月将是令AJAX开发者激动的日子。因此,我非常希望本文能够激起您对AJAX的兴趣,并在你以后构建下一代Web应用程序时优先考虑使用这一技术。


















本文转自朱先忠老师51CTO博客,原文链接: http://blog.51cto.com/zhuxianzhong/59337 ,如需转载请自行联系原作者


相关文章
|
1月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
219 4
|
5月前
|
缓存 JavaScript 前端开发
鸿蒙5开发宝藏案例分享---Web开发优化案例分享
本文深入解读鸿蒙官方文档中的 `ArkWeb` 性能优化技巧,从预启动进程到预渲染,涵盖预下载、预连接、预取POST等八大优化策略。通过代码示例详解如何提升Web页面加载速度,助你打造流畅的HarmonyOS应用体验。内容实用,按需选用,让H5页面快到飞起!
|
5月前
|
JavaScript 前端开发 API
鸿蒙5开发宝藏案例分享---Web加载时延优化解析
本文深入解析了鸿蒙开发中Web加载完成时延的优化技巧,结合官方案例与实际代码,助你提升性能。核心内容包括:使用DevEco Profiler和DevTools定位瓶颈、四大优化方向(资源合并、接口预取、图片懒加载、任务拆解)及高频手段总结。同时提供性能优化黄金准则,如首屏资源控制在300KB内、关键接口响应≤200ms等,帮助开发者实现丝般流畅体验。
|
前端开发 JavaScript Shell
鸿蒙5开发宝藏案例分享---Web页面内点击响应时延分析
本文为鸿蒙开发者整理了Web性能优化的实战案例解析,结合官方文档深度扩展。内容涵盖点击响应时延核心指标(≤100ms)、性能分析工具链(如DevTools时间线、ArkUI Trace抓取)以及高频优化场景,包括递归函数优化、网络请求阻塞解决方案和setTimeout滥用问题等。同时提供进阶技巧,如首帧加速、透明动画陷阱规避及Web组件初始化加速,并通过优化前后Trace对比展示成果。最后总结了快速定位问题的方法与开发建议,助力开发者提升Web应用性能。
|
5月前
|
JSON 开发框架 自然语言处理
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(三)
本文主要介绍了应用开发中的三大核心内容:生命周期管理、资源限定与访问以及多语言支持。在生命周期部分,详细说明了应用和页面的生命周期函数及其触发时机,帮助开发者更好地掌控应用状态变化。资源限定与访问章节,则聚焦于资源限定词的定义、命名规则及匹配逻辑,并阐述了如何通过 `$r` 引用 JS 模块内的资源。最后,多语言支持部分讲解了如何通过 JSON 文件定义多语言资源,使用 `$t` 和 `$tc` 方法实现简单格式化与单复数格式化,为全球化应用提供便利。
252 104
|
5月前
|
JavaScript 前端开发 API
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(二)
本文介绍了HarmonyOS应用开发中的HML、CSS和JS语法。HML作为标记语言,支持数据绑定、事件处理、列表渲染等功能;CSS用于样式定义,涵盖尺寸单位、样式导入、选择器及伪类等特性;JS实现业务逻辑,包括ES6语法支持、对象属性、数据方法及事件处理。通过具体代码示例,详细解析了页面构建与交互的实现方式,为开发者提供全面的技术指导。
271 104
|
5月前
|
开发框架 编解码 JavaScript
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(一)
该文档详细介绍了一个兼容JS的类Web开发范式的方舟开发框架,涵盖概述、文件组织、js标签配置及app.js等内容。框架采用HML、CSS、JavaScript三段式开发方式,支持单向数据绑定,适合中小型应用开发。文件组织部分说明了目录结构、访问规则和媒体文件格式;js标签配置包括实例名称、页面路由和窗口样式信息;app.js则描述了应用生命周期与对象管理。整体内容旨在帮助开发者快速构建基于方舟框架的应用程序。
262 102
|
7月前
|
中间件 Go
Golang | Gin:net/http与Gin启动web服务的简单比较
总的来说,`net/http`和 `Gin`都是优秀的库,它们各有优缺点。你应该根据你的需求和经验来选择最适合你的工具。希望这个比较可以帮助你做出决策。
334 35
|
11月前
|
开发框架 前端开发 JavaScript
ASP.NET Web Pages - 教程
ASP.NET Web Pages 是一种用于创建动态网页的开发模式,采用HTML、CSS、JavaScript 和服务器脚本。本教程聚焦于Web Pages,介绍如何使用Razor语法结合服务器端代码与前端技术,以及利用WebMatrix工具进行开发。适合初学者入门ASP.NET。
|
6月前
|
Web App开发 前端开发 JavaScript
鸿蒙5开发宝藏案例分享---Web适配一多开发实践
这是一份实用的鸿蒙Web多设备适配开发指南,针对开发者在不同屏幕尺寸下的布局难题提供了解决方案。文章通过三大法宝(相对单位、媒体查询和窗口监听)详细介绍如何实现智能适配,并提供了多个实战案例,如宫格布局、对话框变形和自适应轮播图等。此外,还分享了调试技巧及工具推荐,帮助开发者快速上手并优化性能。最后鼓励读者实践探索,并提示更多官方资源等待发现。

热门文章

最新文章