用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 ,如需转载请自行联系原作者


相关文章
|
15天前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
74 3
|
3月前
|
编解码 前端开发 JavaScript
.NET_web前端框架_layui_栅格布局
【8月更文挑战第27天】
42 4
|
3月前
|
开发框架 JavaScript 前端开发
震撼!破解 ASP.NET 服务器控件 Button 执行顺序之谜,颠覆你的开发认知!
【8月更文挑战第16天】在ASP.NET开发中,通过Button控件实现先执行JavaScript再触后台处理的需求十分常见。例如,在用户点击按钮前需前端验证或提示,确保操作无误后再传递数据至后台深度处理。此过程可通过设置Button的`OnClientClick`属性调用自定义JavaScript函数完成验证;若验证通过,则继续触发后台事件。此外,结合jQuery也能达到相同效果,利用`__doPostBack`手动触发服务器端事件。这种方式增强了应用的交互性和用户体验。
44 8
|
2月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
2月前
|
开发框架 .NET 中间件
ASP.NET Core Web 开发浅谈
本文介绍ASP.NET Core,一个轻量级、开源的跨平台框架,专为构建高性能Web应用设计。通过简单步骤,你将学会创建首个Web应用。文章还深入探讨了路由配置、依赖注入及安全性配置等常见问题,并提供了实用示例代码以助于理解与避免错误,帮助开发者更好地掌握ASP.NET Core的核心概念。
74 3
|
2月前
|
开发框架 前端开发 .NET
VB.NET中如何利用ASP.NET进行Web开发
在VB.NET中利用ASP.NET进行Web开发是一个常见的做法,特别是在需要构建动态、交互式Web应用程序时。ASP.NET是一个由微软开发的开源Web应用程序框架,它允许开发者使用多种编程语言(包括VB.NET)来创建Web应用程序。
49 5
|
23天前
|
前端开发 API 数据格式
颠覆传统!AJAX、Fetch API与Python后端,开启Web开发新篇章!
在Web开发领域,技术的快速迭代推动着应用不断进化。传统前后端交互方式已无法满足现代Web应用对高效、实时性和用户体验的需求。AJAX作为异步通信的先驱,使页面无需刷新即可更新部分内容,显著提升用户体验;尽管XML曾是其主要数据格式,但如今JSON已成为主流。Fetch API则以其简洁、灵活的特点成为AJAX的现代替代品,基于Promises的异步请求让开发更加高效。与此同时,Python后端凭借高效稳定和丰富的库支持,成为众多开发者的首选,无论是轻量级的Flask还是全功能的Django,都能为Web应用提供强大的支撑。
33 0
|
2月前
|
开发框架 JavaScript 前端开发
|
3月前
|
人工智能 前端开发 开发工具
NET在企业级应用、Web开发、移动应用、云服务及游戏领域的创新实践
.NET技术自2000年推出以来,在软件开发领域扮演着关键角色。本文从核心优势出发,探讨其统一多语言平台、强大工具集、跨平台能力及丰富生态系统的价值;随后介绍.NET在企业级应用、Web开发、移动应用、云服务及游戏领域的创新实践;最后分析性能优化、容器化、AI集成等方面的挑战与机遇,展望.NET技术的未来发展与潜力。
56 2
|
3月前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
47 0