浏览器可以在用户的硬盘里缓存图片、JavaScript、CSS文件,如果XML HTTP调用是一个HTTP GET的话也是可以缓存的。这个缓存是基于URL的。如果是相同URL,且保存在同一个电脑里,那么数据将从缓存里加载,而不会向服务器再次请求。基本上,浏览器可以缓存任何HTTP GET请求并且返回基于URL的被缓存数据。如果你把一个XML HTTP调用作为HTTP Get方式的话,那么服务端将返回一些特殊的头信息,用于通知浏览器对相应做缓存,之后再次调用相同的内容,结果就会立即从缓存中被返回,从而减少了网络传输延迟和下载时间。
在Pageflakes中,我们对用户的状态做了缓存,所以当用户再次访问的时候会从浏览器的缓存里立即得到缓存数据,而不用通过服务端。因此第二次加载时间会变得非常快。我们也缓存了用户的某些行为所产生的结果。当用户再次做相同行为时,缓存结果就会立即从用户的本地缓存中加载,从而减少了网络传输时间。用户会体验到一个快速加载和高响应的站点,获得速度会有明显增长。
这个方法就是处理Atlas web service调用时要使用HTTP GET方式,并且要返回一些明确的HTTP头信息告知浏览器具体要缓存多长时间。如果在响应期间你返回了一个“Expires”头信息,那么浏览器就会缓存这个XML HTTP结果。这里你需要返回两个头信息去通知浏览器缓存结果。
Expires: Fri, 1 Jan 2030
Cache-Control: public
Cache-Control: private, must-revalidate, proxy-revalidate, max-age=60
public string CachedGet()
{
TimeSpan cacheDuration = TimeSpan.FromMinutes(1);
Context.Response.Cache.SetCacheability(HttpCacheability.Public);
Context.Response.Cache.SetExpires(DateTime.Now.Add(cacheDuration));
Context.Response.Cache.SetMaxAge(cacheDuration);
Context.Response.Cache.AppendCacheExtension(
"must-revalidate, proxy-revalidate");
return DateTime.Now.ToString();
}
输出的结果依然是错的,并没有被缓存
简短节说。反编译HttpCachePolicy类(Context.Response.Cache对象的类)之后,我发现了如下代码:
public string CachedGet2()
{
TimeSpan cacheDuration = TimeSpan.FromMinutes(1);
FieldInfo maxAge = Context.Response.Cache.GetType().GetField("_maxAge",
BindingFlags.Instance|BindingFlags.NonPublic);
maxAge.SetValue(Context.Response.Cache, cacheDuration);
Context.Response.Cache.SetCacheability(HttpCacheability.Public);
Context.Response.Cache.SetExpires(DateTime.Now.Add(cacheDuration));
Context.Response.Cache.AppendCacheExtension(
"must-revalidate, proxy-revalidate");
return DateTime.Now.ToString();
}
{
TestService.CachedGet(function(result)
{
debug.trace(result);
});
}
<trust level="Medium"/>
<trust level="Full"/>
Atlas回调函数不会在它们被调用的相同上下文中执行 。例如,如果你在一个JavaScript类里像这样使用一个web method
{
this.id = 1;
this.call = function()
{
TestService.DoSomething( "Hi", function(result)
{
debug.dump( this.id );
} );
}
}
原因是这样的。我们知道只要JavaScript事件被触发,那么“this”就是指导致事件发生的那个HTML元素,所以如果你像下面这样写的话:
{
this.id = 1;
this.call = function()
{
TestService.DoSomething( "Hi", function(result)
{
debug.dump( this.id );
} );
}
}
<input type="button" id="ButtonID" onclick="o.onclick" />
同样的,当XML HTTP触发了可以捕获和激发回调函数的onreadystatechanged事件的时候,代码执行仍然在XML HTTP的上下文中。它是触发了这个事件的XML HTTP对象。结果,“this”就指向了XML HTTP对象,而不是你自己的在回调函数被声明处的类。
为了使回调函数在类的实例的上下文中激发,所以要让“this”指向类的实例,你需要做如下改变。
{
this.id = 1;
this.call = function()
{
TestService.DoSomething( "Hi",
Function.createDelegate( this, function(result)
{
debug.dump( this.id );
} ) );
}
}
return function() {
return method.apply(instance, arguments);
}
}
默认情况下,ASP.NET AJAX的所有web service调用都使用HTTP POST方式。HTTP POST方式要比HTTP GET方式付出更多的代价,它通过网络传输更多的字节,因此就要占用宝贵的网络传输时间,也使得ASP.NET要在服务端做一些额外处理。所以,在可能的情况下你应该使用HTTP GET方式。但是,HTTP GET方式不允许你将对象作为参数传输,你只能传输数字、字符串和日期。当你处理一个HTTP GET调用的时候,Atlas会构造一个被编码的URL并使用它。所以,你不应该传输很多内容而使URL超过2048个字符。据我目前所知,这是任何URL的最大长度。
为了在一个web service方法中使用HTTP GET方式,你需要用[ScriptMethod(UseHttpGet=true)]属性修饰这个方法:
public string HelloWorld()
{
}
Ethereal是一个很好的工具,它可以侦测到POST和GET的情况下到底发生了什么:
所以,当你要从服务端下载页的某一部分、一个表格或者是一段文本之类的时候就应该使用HTTP GET方式。而如果要像web form那样以提交的方式发送数据到服务端的话就不应该使用HTTP GET方式。
结论
上面所说的这些高级技巧都已经在Pageflakes中实现了,这里我并没有提及它的详细实现方法,但是原理都提到了,所以,你可以放心地使用这些技术。这些技术将节省你解决问题的时间,也许在开发环境中你从来没认识到这些问题,但是当你大规模部署网站之后,来自世界各地的访问者就将面对这些问题。一开始就正确的实现这些技巧将会大大节省你的开发和客户支持的时间。请同时关注我的博客以获得更多的技巧。