因为一些原因,要将原来两个独立的站点(假设为dh.site.com和cd.site.com)放到同一个站点的两个application下,分别为(www.site.com/dh和www.site.com/cd)。
因为在开发的时候,大部分静态文件的引用路径都是采用绝对路径的形式,例如/style/css1.css。可想而知,当将两个站点作为两个application放置后,这些静态文件就变得无法访问了,因为文件真实的路径已经变成了/dh/style.css1.css了。解决这个问题的最直接的方法就是修改所有的绝对路径:要么将原来的路径加上application的虚拟根路径(例如将/style.css1.css变成/dh/style.css1.css),要么改成相对路径。但无论如何,直接修改路径的方式都要找出散布在各个角落里的路径查找出来,再进行修改。虽然利用IDE的查找替换功能可以很快将路径查找出来,但也难免查找不完全,另一方面,如果路径数量非常多,则不得不花费大量时间去修改并且在修改后进行检查排错。
尝试一:使用http module进行路径重写。
这也是非常直接地联想到的方法:使用http module(可以使用url rewriter,或者自己编写一个http module)对正在进行访问的url进行重写到正确的路径。但这使用http module进行重写会有一个问题:只能用作重写静态文件请求,而对于aspx之类的动态文件的请求,因为在IIS6或以上,不同应用程序运行在不同的应用程序域(app domain)中,因此对动态文件的请求进行重写转跳,会发现两个转跳对象的session根本不同,或者根本不能加载类型(root application可以加载子application的类型)。这无论从网站安全角度,还是程序都安全角度来说,都是合理的。
因为这里需要自定义更多的东西,因此需要自己编写一个http module,而不是使用url rewriter。思路如下:因为cd和dh中的静态文件都是指向到虚拟根目录的,因此需要在站点的root application中使用该http module。而在http module中,根据静态文件请求的referrer来判断请求时来自dh还是cd,然后再重定向到dh或者cd下相对应的文件。
http module中的代码:
public class RedirectModule : IHttpModule { //定义静态文件的扩展名数组 static string[] staticsFilesExName=new string[]{".jpg",".jepg",".gif",".png",".css",".html",".js"}; void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; if (app.Context.Request.UrlReferrer != null) { //取得页面的referrer,进行判断请求是来自www.site.com/dh还是来自www.site.com/cd string referrer = app.Context.Request.UrlReferrer.AbsoluteUri; //取得application root path部分,也就是http://www.site.com/dh中的"/dh"部分 string appRootPath = referrer.Substring(23, 3); string rawUrl = app.Request.RawUrl; //得到转跳的url string reUrl = appRootPath + rawUrl; string extendName = app.Request.Path.Substring(app.Request.Path.LastIndexOf(".")); //判断请求的对象是否为静态文件,是则转跳。 if (RedirectModule.staticsFilesExName.Contains(extendName.ToLower())) { app.Context.RewritePath(reUrl); } } } public void Dispose() { } public void Init(HttpApplication context) { context.BeginRequest += new EventHandler(context_BeginRequest); } }
注:在IIS7.5中,为了能使http module正常工作,除了在web.config/httpModules节下添加http module外,必须将站点的应用程序池版本.net framework版本设置为V2.0,并且将托管管道模式设置经典(classic),此外,还可能需要在system.webServer/handlers节中添加配置:
<add name="wildcard" path="*" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="None" preCondition="classicMode,runtimeVersionv2.0,bitness32" />
然而,虽然使用http module能解决静态文件访问的问题,但不能重写动态文件,所以这不是个完美的解决方案。当然,如果动态文件的url数量少,手动改起来工作也不大的话,这也是个可以接受的方法。
另外,可以考虑一下使用isapi filter来实现请求url重写,而且因为不同于http module要受到.net的限制,isapi filter运行在IIS层次之中,是否能突破应用程序池的限制,重定向的文件能顺利被执行(就如同该请求事来自浏览器一样)?