[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的代码,开始的代码是这样的:       

IHttpHandler handler1; 
 
if (context != null
      

            virtualPath 
= context.Request.BaseDir.Combine(virtualPath); 
      }
 

     由于问题与访问路径有关,所以我格外关注上面的代码,上面的代码是对请求的虚拟路径进行处理。让我们看看context.Request.BaseDir的代码:

internal VirtualPath BaseDir 
    

      
get 
      

            
if (this._baseVirtualDir == null
            

                  
this._baseVirtualDir = this.FilePathObject.Parent; 
            }
 
            
return this._baseVirtualDir; 
      }
 
    }
 

     FilePathObject.Parent是System.Web.VirtualPath的Parent属性,在Parent属性中开始有这样的代码:

if (this.IsRoot)
            
{
                  
return null;
            }

     当以http://localhost/这样的根目录方式访问时,并且在IIS中设置中通配符映射(如果没有设置通配符映射,IIS会自动加上默认文档),这里IsRoot就是true,从IsRoot的代码可以看出:

public bool 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。 
     不知是否有其他方法避免这个问题,继续研究。

,-1,0,93,NULL,0 53353,343460,[ASP.NET 2.0]PageParser.GetCompiledPageInstance中的一个Bug及解决方法,2006-03-05 21:06:00,NULL,1,dudu,duyong@yz2pp.com,218.91.132.198,0,NULL,2006-03-06 23:49:00,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的代码,开始的代码是这样的:       

IHttpHandler handler1; 
 
if (context != null
      

            virtualPath 
= context.Request.BaseDir.Combine(virtualPath); 
      }
 

     由于问题与访问路径有关,所以我格外关注上面的代码,上面的代码是对请求的虚拟路径进行处理。让我们看看context.Request.BaseDir的代码:

internal VirtualPath BaseDir 
    

      
get 
      

            
if (this._baseVirtualDir == null
            

                  
this._baseVirtualDir = this.FilePathObject.Parent; 
            }
 
            
return this._baseVirtualDir; 
      }
 
    }
 

     FilePathObject.Parent是System.Web.VirtualPath的Parent属性,在Parent属性中开始有这样的代码:

if (this.IsRoot)
            
{
                  
return null;
            }

     当以http://localhost/这样的根目录方式访问时,并且在IIS中设置中通配符映射(如果没有设置通配符映射,IIS会自动加上默认文档),这里IsRoot就是true,从IsRoot的代码可以看出:

public bool 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.ToLower().IndexOf(".aspx"< 0)
           {
               
if (!url.EndsWith("/"))
               {
                   url 
+= "/";
               }
               url 
+= "default.aspx" ;
               context.RewritePath(url);
               
return PageParser.GetCompiledPageInstance(url, pagepath, context);
           }
0
0
(请您对文章做出评价)
« 上一篇:[致歉]程序升级影响了博客园的正常访问
» 下一篇:ASP.NET 2.0, 想说爱你不容易—在ASP.NET 2.0中开发通配符映射应用程序的一些问题
posted @ 2006-03-07 03:39 dudu 阅读(4218) 评论(7)  编辑 收藏 网摘 所属分类: ASP.NET

  回复  引用  查看    
#1楼2006-03-07 17:36 | 韦恩卑鄙      
1.1升级到2.0好多工程会变得不伦不类需要手动改5555 加油啊
  回复  引用  查看    
#2楼[楼主]2006-03-07 23:06 | dudu      
@韦恩卑鄙
你可以参考这篇文章中的方法: http://dudu.cnblogs.com/archive/2006/02/18/333115.html

  回复  引用    
#3楼2006-03-10 19:07 | Activer[未注册用户]
如果说 ASP.NET 1.1可以用的,ASP.NET 2.0 不能用,这个叫做不兼容,应该算不上Bug

如果要说到PageParser.GetCompiledPageInstance,微软本来就是说不要用这个东西,你偏偏要用,如果说喜欢自己搞编译控制,在.NET 1.1里面用用PageParser.GetCompiledPageInstance就可以了,为什么还要到.NET 2.0里面用?

“当以http://localhost/这样的根目录方式访问时,并且在IIS中设置中通配符映射(如果没有设置通配符映射,IIS会自动加上默认文档),”

如果没有设置通配符映射,是IIS在做这个控制,现在你设置了通配符映射,你却不去做这样的控制,却说.NET 不给你做好。.NET真是怨,你要控制 就给你控制了,给你控制 你却不去控制,却说.NET没有控制好

  回复  引用  查看    
#4楼[楼主]2006-03-10 21:56 | dudu      
  回复  引用    
#6楼2008-03-10 22:52 | gzjl[未注册用户]
为何我按照你的解决方法在PageParser.GetCompiledPageInstance之前进行RewritePath处理,根目录中运行没有问题了,但是点任何一个随笔均出现"the entry could not be found or has been removed"
  回复  引用    
#7楼2009-09-17 22:37 | fengwf[未注册用户]
我有一个需求,就是访问.ashx之前加一步验证,参数包含字符baidu.com的我允许程序继续执行,否则停止.
总是报错 不是有效的虚拟路径.不知道是不是成了死循环了? 请帮帮我
web.config
<httpHandlers>
<add verb="*" path="*.ashx" type="myWeb.BasicHandler, BasicHandler"/>
</httpHandlers>

BasicHandler.cs

using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Compilation;

namespace myWeb
{
/// <summary>
/// $验证失败$ 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class BasicHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string rawUrl = context.Request.RawUrl;
if (rawUrl.indexOf("baidu.com")>-1)
{
string rawUrl = context.Request.RawUrl;
IHttpHandler hander = BuildManager.CreateInstanceFromVirtualPath("~/"+rawUrl, typeof(IHttpHandler)) as IHttpHandler;
hander.ProcessRequest(context);
return;
}
else
context.Response.Write("验证失败的");
}

public bool IsReusable
{
get
{
return false;
}
}
}
}

,我想应该可以内部执行把,可是好象是死循环.

有点这种的意思
<urlMappings enabled="true">
<add url="~/1.aspx" mappedUrl="~/1.aspx"/>
</urlMappings>