您的位置:首页 > 编程语言 > ASP

ASP.NET AJAX之内部揭秘

2007-10-18 22:16 459 查看
介绍
微软最近发布了ASP.NET AJAX的Beta 2版。虽然它是一个非常强大的框架,但是当你在web 2.0的世界中要开发一个真正的AJAX web站点的话,就会遇到很多问题,而且你几乎找不到任何相关文档。本文中,我将介绍一些在开发Pageflakes中所学习到的高级经验。我们将会看到ASP.NET AJAX一些功能的优缺点,如批调用(Batch Call),调用超时,浏览器调用拥堵问题,ASP.NET 2.0中web service响应缓存的bug等等。

本文最后更新:2006年12月22日,针对ASP.NET AJAX RC更新(译注:适用于ASP.NET AJAX v1.0)

为什么要使用ASP.NET AJAX
一些人在看到Pageflakes的时候,首先问我的问题就是“为什么不使用Protopage或者Dojo库?而是Atlas?”微软的Atlas(已重命名为ASP.NET AJAX)是一个非常有前途的AJAX框架。微软为了这个框架做了很多努力,制作了大量可重用的组件,这样可以减少你的开发时间,这样的话,只要很少的工作量即可让你的web应用程序有一个完美的界面。它与ASP.NET融为一体,并且兼容ASP.NET的Membership和Profile。AJAX Control Toolkit项目包括了28个扩展(译注:现在有34个),你可以把它们拖拽到页面上,然后通过设置一些属性就可以产生非常酷的效果。看看那些例子你就会知道ASP.NET AJAX给我们带来了多么强大的功能。

在我们最初开发Pageflakes的时候,Atlas还处于幼儿阶段。我们只能使用page method和Web Service方法来调用Atlas的特性。我们不得不开发我们自己的拖拽、组件构造、弹出、伸缩/展开等特性。但是现在,Atlas提供了所有这些功能,所以可以大大节省我们的开发时间。最令人惊奇的是Atlas提供了web service代理的特性。你可以指定<script>标签到一个.asmx文件,然后你会得到一个JavaScript类,它会根据web service的定义被正确的生成。这使得添加或移除一个web service变得非常简单,而且你在web service中添加或移除方法都不需要客户端作任何改变。Atlas也提供了很多基于AJAX调用的控件,并且提供了在JavaScript中捕获丰富异常信息的特性。服务器端的异常可以被精确的抛给客户端的JavaScript代码,你可以捕获它们并格式化这些错误信息,然后显示给用户。Atlas与ASP.NET 2.0结合起来工作得非常出色,完全消除了整合问题。你不需要担心page method和web service的验证和授权问题,所以可以大大减少你的客户端代码的开发量(当然,也正是因为如此,Atlas运行时也变得非常巨大),相对于其它的AJAX框架来说,你可以把更多的精力放到自己的代码开发上来。

Atlas最近的版本可以与ASP.NET的Membership、Profile完美的结合,为你提供了在JavaScript中登录和注销的特性,而不用发postback给服务器,你也可以直接从JavaScript中读取和写入Profile。当你在web应用程序中使用Membership和Profile的时候,这将变得非常容易,例如我们做的Pageflakes

在Atlas的早些版本中,没有使用HTTP GET调用的方法。所有的调用都是HTTP POST,所以调用的代价是非常大的。而现在,你可以说,哪个调用是HTTP GET的(而哪个不是)。一旦你使用了HTTP GET,你就可以利用HTTP响应缓存特性,我将很快介绍这一特性。

批调用并不一定快
ASP.NET AJAX的CTP版本(之前的版本)里有一个特性,就是允许在一个请求里包含多个请求。它工作时你不会注意到任何事情,而且也不需要写任何特殊的代码。一旦你使用了批调用特性,那么在一次批调用期间,其中所有的web service调用都会被执行,所以它将减少回发时间和总响应时间。

实际的响应时间可能减少了,但是我们感觉到的延迟却变长了。如果有3个web service被批量调用,那么第一个调用不会最先完成,而是所有3个调用会在相同的时间完成。如果你的每一个web service调用完成后都更新UI的话,将不会一步一步更新,而是在所有调用一起完成后再一起更新UI。结果,你不会看到UI被快速更新,而是在UI被更新之前有一个长时间的延迟。如果说调用中的任何一个(比如第3个调用)下载了大量数据,那么在所有的3个调用完成之前用户不会看到任何变化。所以第一个调用的执行时间几乎接近3个调用的总执行时间。虽然减少了实际的总计处理时间,但是会感觉有更长的延迟。当每个调用都只传输少量数据的时候批调用是非常好的,这样,3个小型调用就会在一次回发中执行完。

让我们看看3个调用是如何一个一个被完成的,这将说明这些调用实际上是如何被执行的。
function TestTimeout()
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
Sys.Net.WebServiceProxy.retryOnFailure =
function(result, userContext, methodName, retryParams, onFailure)

Sys.Net.WebServiceProxy.original_invoke = Sys.Net.WebServiceProxy.invoke;
Sys.Net.WebServiceProxy.invoke =
function Sys$Net$WebServiceProxy$invoke(servicePath, methodName, useGet,
params, onSuccess, onFailure, userContext, timeout)

QueuedCall = function( servicePath, methodName, useGet, params,
onSuccess, onFailure, userContext, timeout )

QueuedCall.prototype =
Sys.Net.WebServiceProxy.original_invoke = Sys.Net.WebServiceProxy.invoke;
Sys.Net.WebServiceProxy.invoke =
function Sys$Net$WebServiceProxy$invoke(servicePath, methodName,
useGet, params, onSuccess, onFailure, userContext, timeout)
HTTP/1.1 200 OK
Expires: Fri, 1 Jan 2030
Cache-Control: public
该信息将通知浏览器要缓存结果直到2030年1月1日。在你处理具有相同参数的同一个XML HTTP调用的时候,就将从电脑的缓存中加载数据,而不会通过服务端。这里还有更多的控制缓存的高级方法。例如,有一个头信息通知浏览器缓存60秒,那么浏览器要在60秒之后才能接触到服务端并获取新的结果。当60秒后浏览器本地缓存过期的时候,它也会防止从代理服务器端获得已缓存的响应。

HTTP/1.1 200 OK
Cache-Control: private, must-revalidate, proxy-revalidate, max-age=60
让我们来尝试着在一个ASP.NET web service方法中产生这样的头信息:

[WebMethod][ScriptMethod(UseHttpGet=true)]
public string CachedGet()
[WebMethod][ScriptMethod(UseHttpGet=true)]
public string CachedGet2()
function testCache()
<system.web>
<trust level="Medium"/>
它会防止我们设置响应对象的_maxAge,因为它需要反射。所以你必须删除trust元素或者设置它的level属性为Full

<system.web>
<trust level="Full"/>

当“this”不是你认为的“this”的时候
Atlas回调函数不会在它们被调用的相同上下文中执行 。例如,如果你在一个JavaScript类里像这样使用一个web method

function SampleClass()
function SampleClass()

<input type="button" id="ButtonID" onclick="o.onclick" />
如果你单击了这个按钮,就会发现“ButtonID”代替了“1”。原因就是这个按钮正在调用“call”方法。所以,这个调用在按钮对象的上下文中完成,而“this”就被映射为这个按钮对象。

同样的,当XML HTTP触发了可以捕获和激发回调函数的onreadystatechanged事件的时候,代码执行仍然在XML HTTP的上下文中。它是触发了这个事件的XML HTTP对象。结果,“this”就指向了XML HTTP对象,而不是你自己的在回调函数被声明处的类。

为了使回调函数在类的实例的上下文中激发,所以要让“this”指向类的实例,你需要做如下改变。

function SampleClass()
[WebMethod] [ScriptMethod(UseHttpGet=true)]
public string HelloWorld()
}
POST与GET的另一个问题是,POST需要两次网络传输。当你使用POST的时候,web服务器会先发送一个“HTTP 100 Continue”,这意味着web服务器已经准备好了接收内容。之后,浏览器才会发送实际数据。所以,因为POST请求的初始阶段要比GET方式花费更多的时间,在AJAX程序里网络延迟(你的电脑和服务器之间的数据传输)是要给与足够重视的,因为AJAX适合处理一些小的需要在毫秒级的时间内完成的调用。否则程序会不流畅并且让用户觉得厌烦。

Ethereal是一个很好的工具,它可以侦测到POST和GET的情况下到底发生了什么:



从上面的图中,你可以看到POST方式在准备发送实际数据之前,要从web服务器请求一段“HTTP 100 Continue”的确认信息,这之后才会传输数据。另一方面,GET方式传输数据是不需要任何确认的。

所以,当你要从服务端下载页的某一部分、一个表格或者是一段文本之类的时候就应该使用HTTP GET方式。而如果要像web form那样以提交的方式发送数据到服务端的话就不应该使用HTTP GET方式。

结论
上面所说的这些高级技巧都已经在Pageflakes中实现了,这里我并没有提及它的详细实现方法,但是原理都提到了,所以,你可以放心地使用这些技术。这些技术将节省你解决问题的时间,也许在开发环境中你从来没认识到这些问题,但是当你大规模部署网站之后,来自世界各地的访问者就将面对这些问题。一开始就正确的实现这些技巧将会大大节省你的开发和客户支持的时间。请同时关注我的博客以获得更多的技巧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: