优化WCF服务代理的浏览器缓存策略

在Web应用程序中,WCF(Windows Communication Foundation)服务是一种常用的服务交互方式。然而,当使用WCF服务时,浏览器需要下载对应的JavaScript代理文件,这可能会导致页面加载延迟和渲染性能下降。这是因为浏览器会同步地、一个接一个地下载这些JavaScript文件。此外,由于生成的JavaScript代理文件没有在浏览器上进行缓存,所以每次访问相同页面时,都需要重新下载这些文件。

问题分析

通过Fiddler工具追踪页面使用两个WCF服务的情况,可以发现存在两个对/js的连续请求。即使在相同的浏览器会话中,每次访问相同页面时,都会产生这两个请求。当第二次浏览同一页面时,除了WCF JavaScript代理之外,其他所有内容都被缓存了。由于WCF JavaScript代理生成器没有产生必要的缓存头信息,因此这些代理文件不会被浏览器缓存

解决方案

为了确保生成的JavaScript代理在浏览器上被缓存,并且当服务被访问时,如果Service.svc文件没有变化,则返回HTTP 304响应,可以采用以下解决方案。

实现一个IIS和IIS Express的HttpModule,该模块将拦截对WCF服务代理的调用。它首先检查自浏览器上缓存的版本以来服务是否已更改。如果没有更改,则返回HTTP 304,并且不会执行服务代理生成过程。这样不仅节省了服务器上的CPU资源,而且如果请求是第一次并且浏览器上没有缓存的副本,它将提供代理并发出适当的缓存头信息,以便在浏览器上缓存响应。

为了使服务被缓存,首先需要改变引用服务的方式。在ASP.NET中,可以通过以下方式添加服务引用:

<asp:ScriptManager runat="server"> <Services> <asp:ServiceReference Path="~/(v2)/Service1.svc" /> </Services> </asp:ScriptManager>

注意服务引用路径中的(v2),这是一个版本号。每当服务发生变化并且希望浏览器下载最新副本时,就需要更改这个版本号。如果没有这个版本号在URL上,服务代理就不会被缓存。这是一种安全机制,防止JavaScript代理在浏览器上被缓存,并且除非更改服务名称,否则无法更新它们。

HttpModule首先拦截BeginRequest事件,以查看请求的URL是否是WCF服务代理,并且是否已版本化。如果是,则检查浏览器上缓存的副本是否已过期。如果.svc文件在此期间已更改,则缓存的副本已过期。如果没有,则返回HTTP 304并结束请求。

public sealed class JavascriptProxyCacheModule : IHttpModule { private HttpApplication app; public void Init(HttpApplication context) { app = context; app.EndRequest += new EventHandler(app_EndRequest); app.BeginRequest += new EventHandler(app_BeginRequest); } void app_BeginRequest(object sender, EventArgs e) { var path = app.Context.Request.Path.ToLower().Replace("\\", "/"); if (IsWcfJavacsriptProxy(path)) { int pos = path.IndexOf("("); if (pos > 0) { int endPos = path.IndexOf(")"); path = path.Substring(0, pos) + path.Substring(endPos + 2); app.Context.RewritePath(path); string ifModifiedSince = app.Context.Request.Headers["If-Modified-Since"]; if (!string.IsNullOrEmpty(ifModifiedSince)) { string filePath = app.Context.Request.PhysicalPath; DateTime fileLastModifyDateTime = File.GetLastWriteTime(filePath); DateTime browserLastModifyDateTime; if (DateTime.TryParse(ifModifiedSince, out browserLastModifyDateTime)) { if ((browserLastModifyDateTime - fileLastModifyDateTime).Seconds > 5) { app.Context.Response.StatusCode = 304; app.Context.Response.End(); } } } } } } private void app_EndRequest(object sender, EventArgs e) { var path = app.Context.Request.Path.ToLower().Replace("\\", "/"); if (app.Context.Response.StatusCode == 200 || app.Context.Response.StatusCode == 202) { if (IsWcfJavacsriptProxy(path) && IsVersioned()) { var lastModified = DateTime.UtcNow; var expires = lastModified.AddDays(1).Subtract(lastModified.TimeOfDay).AddHours(7); HttpCachePolicy cache = app.Context.Response.Cache; cache.SetLastModified(lastModified); cache.SetExpires(expires); cache.SetCacheability(HttpCacheability.Public); } } } }

要使用这个HttpModule,需要在web.config文件中添加以下配置:

<httpModules> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add name="JavascriptProxyCacheModule" type="WcfService1.JavascriptProxyCacheModule" /> </httpModules>
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485