[ASP.NET 2.0]PageParser.GetCompiledPageInstance中的一个Bug及解决方法
最近在将博客园的程序迁移到ASP.NET 2.0,本来在本机虚拟目录中运行时可以正常访问首页,比如:http://localhost/blog。
可到在服务器上测试时,访问地址:http://test.cnblogs.com,却出现“Object reference not set to an instance of an object”异常,异常产生于 System.Web.UI.PageParser.GetCompiledPageInstance(VirtualPath virtualPath, String inputFile, HttpContext context) 。
而通过http://test.cnblogs.com/default.aspx却能正常访问,服务器上与本机测试环境主要不同是服务器上程序是运行在IIS根目录,而本机是运行在虚拟目录中。然后,我在本机上将程序放在IIS根目录中运行,出现了同样的问题。
于是,我用Reflector查看System.Web.UI.PageParser.GetCompiledPageInstance的代码,开始的代码是这样的:
if (context != null)
{
virtualPath = context.Request.BaseDir.Combine(virtualPath);
}
由于问题与访问路径有关,所以我格外关注上面的代码,上面的代码是对请求的虚拟路径进行处理。让我们看看context.Request.BaseDir的代码:
{
get
{
if (this._baseVirtualDir == null)
{
this._baseVirtualDir = this.FilePathObject.Parent;
}
return this._baseVirtualDir;
}
}
FilePathObject.Parent是System.Web.VirtualPath的Parent属性,在Parent属性中开始有这样的代码:
{
return null;
}
当以http://localhost/这样的根目录方式访问时,并且在IIS中设置中通配符映射(如果没有设置通配符映射,IIS会自动加上默认文档),这里IsRoot就是true,从IsRoot的代码可以看出:
{
get
{
return (this._virtualPath == "/");
}
}
从上面的代码可以看出,当FilePathObject.Parent为null时,context.Request.BaseDir的值就是null,执行GetCompiledPageInstance中的context.Request.BaseDir.Combine(virtualPath); 就会引起“Object reference not set to an instance of an object”异常。
我觉得这是一个Bug,因为既然context.Request.BasDir有可能为null,就应该在context.Request.BaseDir.Combine(virtualPath)之前对context.Request.BasDir的值进行检查,对null值情况进行处理,或者在System.Web.VirtualPath的Parent属性中当IsRoot为true是直接返回“/”,而不是null,或者在System.Web.HttpRequest.BasDir中对_baseVirtualDir进行null检查,我觉得还是在HttpRequest中进行处理比较好,因为还有其他地方会调用System.Web.HttpRequest.BasDir,所以更准确地说应该是System.Web.HttpRequest的一个Bug。而在ASP.NET 1.1中就不存在这个bug,因为ASP.NET 1.1中System.Web.HttpRequest.BasDir不会返回null。
不知是否有其他方法避免这个问题,继续研究。
最近在将博客园的程序迁移到ASP.NET 2.0,本来在本机虚拟目录中运行时可以正常访问首页,比如:http://localhost/blog。
可到在服务器上测试时,访问地址:http://test.cnblogs.com,却出现“Object reference not set to an instance of an object”异常,异常产生于 System.Web.UI.PageParser.GetCompiledPageInstance(VirtualPath virtualPath, String inputFile, HttpContext context) 。
而通过http://test.cnblogs.com/default.aspx却能正常访问,服务器上与本机测试环境主要不同是服务器上程序是运行在IIS根目录,而本机是运行在虚拟目录中。然后,我在本机上将程序放在IIS根目录中运行,出现了同样的问题。
于是,我用Reflector查看System.Web.UI.PageParser.GetCompiledPageInstance的代码,开始的代码是这样的:
if (context != null)
{
virtualPath = context.Request.BaseDir.Combine(virtualPath);
}
由于问题与访问路径有关,所以我格外关注上面的代码,上面的代码是对请求的虚拟路径进行处理。让我们看看context.Request.BaseDir的代码:
{
get
{
if (this._baseVirtualDir == null)
{
this._baseVirtualDir = this.FilePathObject.Parent;
}
return this._baseVirtualDir;
}
}
FilePathObject.Parent是System.Web.VirtualPath的Parent属性,在Parent属性中开始有这样的代码:
{
return null;
}
当以http://localhost/这样的根目录方式访问时,并且在IIS中设置中通配符映射(如果没有设置通配符映射,IIS会自动加上默认文档),这里IsRoot就是true,从IsRoot的代码可以看出:
{
get
{
return (this._virtualPath == "/");
}
}
从上面的代码可以看出,当FilePathObject.Parent为null时,context.Request.BaseDir的值就是null,执行GetCompiledPageInstance中的context.Request.BaseDir.Combine(virtualPath); 就会引起“Object reference not set to an instance of an object”异常。
我觉得这是一个Bug,因为既然context.Request.BasDir有可能为null,就应该在context.Request.BaseDir.Combine(virtualPath)之前对context.Request.BasDir的值进行检查,对null值情况进行处理,或者在System.Web.VirtualPath的Parent属性中当IsRoot为true是直接返回“/”,而不是null,或者在System.Web.HttpRequest.BasDir中对_baseVirtualDir进行null检查,我觉得还是在HttpRequest中进行处理比较好,因为还有其他地方会调用System.Web.HttpRequest.BasDir,所以更准确地说应该是System.Web.HttpRequest的一个Bug。而在ASP.NET 1.1中就不存在这个bug,因为ASP.NET 1.1中System.Web.HttpRequest.BasDir不会返回null。
不知是否有其他方法避免这个问题,继续研究。
[2006年3月6日修改]已经找到一种解决方法:在PageParser.GetCompiledPageInstance之前进行RewritePath处理,示例代码如下:
{
if (!url.EndsWith("/"))
{
url += "/";
}
url += "default.aspx" ;
context.RewritePath(url);
return PageParser.GetCompiledPageInstance(url, pagepath, context);
}