我的CSDN博客:http://blog.csdn.net/bitfan我的新浪微博:http://t.sina.com.cn/jinxuliang

金旭亮

让技术变得有趣,将学习升级为探索
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ASP.NET MVC2 异常处理机制中令人费解的HTTP 500错误

Posted on 2009-12-10 17:15  金旭亮  阅读(5778)  评论(8编辑  收藏  举报

ASP.NET MVC2 异常处理机制中令人费解的HTTP 500错误

 

Visual Studio 2010 BETA2所集成之ASP.NET MVC应用程序中,可以使用HandleErrorAttribute来捕获并处理异常。

但在默认情况下,用于显示异常的视图(Error.aspx)必须应用母版页(Site.Master),否则,如果尝试使用一个普通的视图来显示ASP.NET MVC应用程序异常信息,会报告“HTTP 500内部服务器错误”

 

 

 

       异常信息显示视图必须应用母版页,这是一个很让人郁闷的限制。

       为什么会有这个限制?

       使用Reflector反汇编HandleErrorAttributeOnException()方法,此方法将会在控制器中的Action方法有异常抛出时被ASP.NET MVC自动调用:

 

public virtual void OnException(ExceptionContext filterContext)

{

   //……代码略

       //生成用于显示异常信息的ViewResult

    filterContext.Result = new ViewResult { ViewName = this.View,

    MasterName = this.Master, ViewData = new    ViewDataDictionary<HandleErrorInfo>(model),

    TempData = filterContext.Controller.TempData };

    filterContext.ExceptionHandled = true;

    filterContext.HttpContext.Response.Clear();

    filterContext.HttpContext.Response.StatusCode = 500;

    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;

            }

    }

 

       可以看到,在生成用于显示异常信息的ViewResult之后,设置filterContextExceptionHandled=true,这将通知后继的异常捕获过滤器(如果有的话):本应用程序抛出的异常已经被处理过了,其他的人可以不用理会这个异常了。

       比较令人费解的是紧接着后面有3句代码引发了HTTP 500错误。

       经测试发现,如果ViewResult实例化的视图对象应用了母版页时,ASP.NET MVC会“吃掉”这一HTTP 500错误。

       然而,如果要实例化的视图对象是独立的,上述3句代码就“真”的引发了HTTP 500错误

       ASP.NET MVC的开发者为什么要加入这3句代码?笔者百思不得其解。

       尽管不知道原因,但我们可以通过编写一个自定义异常处理类来“屏蔽”掉上述3句引发麻烦的代码。其思路是这样的:

       HandleErrorAttribute派生一个自定义类MyErrorHandleAttribute,重写其基类的OnException方法。然后,给其添加一个bool类型的UseMasterPage属性,当其为false时,跳过上面3句引发麻烦的代码。

       MyErrorHandleAttribute的使用方法与HandleErrorAttribute一模一样,只是多了一个UseMasterPage属性:

 

[MyErrorHandle(ExceptionType=typeof(NullReferenceException),

        View="ShowErrorUseViewPage",UseMasterPage=false)]

    [MyErrorHandle(ExceptionType=typeof(ApplicationException),

        View = "ShowErrorUseMasterPage",UseMasterPage=true)]

    public class HomeController : Controller

    {

        public ActionResult Index()

        {

            return View();

        }

        public ActionResult ThrowNullReferenceException()

        {

            throw new NullReferenceException();

        }

         public ActionResult ThrowApplicationException()

        {

            throw new ApplicationException();

        }

}

 

其中ShowErrorUseViewPage.aspx是普通视图,没有应用母版。

以下是MyErrorHandleAttribute类的代码:

 

public class MyErrorHandle : HandleErrorAttribute

{

    public bool UseMasterPage

    {

        get;

        set;

    }

  

  public override void OnException(ExceptionContext filterContext)

    {

        if (filterContext == null)

        {

            throw new ArgumentNullException("filterContext");

        }

        if (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled)

        {

            Exception innerException = filterContext.Exception;

            if ((new HttpException(null, innerException).GetHttpCode() == 500) && this.ExceptionType.IsInstanceOfType(innerException))

            {

                string controllerName = (string)filterContext.RouteData.Values["controller"];

                string actionName = (string)filterContext.RouteData.Values["action"];

                HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);

                filterContext.Result = new ViewResult { ViewName = this.View, MasterName = this.Master, ViewData = new ViewDataDictionary<HandleErrorInfo>(model), TempData = filterContext.Controller.TempData };

                filterContext.ExceptionHandled = true;

                if (UseMasterPage)

                {

                    filterContext.HttpContext.Response.Clear();

                    filterContext.HttpContext.Response.StatusCode = 500;

                    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;

                }

            }

        }      

    }

}

 

除了加方框的代码,其余部分都是原样复制于Reflector反汇编出来的代码。

将保存了上述代码的“MyErrorHandleAttribute.cs”文件添加到ASP.NET MVC项目中,就可以在控制器中使用此自定义的异常处理类了。

在以下环境中MyErrorHandleAttribute均工作正常:

1)使用VS2010 BETA2开发和调试ASP.NET MVC2项目:

2)将ASP.NET MVC2项目发布到IIS 7.5(应用ASP.NET 4.0集成模式)上以HTTP方式访问。

       需要指出的是,在笔者的测试中,发现现有版本的HandleErrorAttribute并非始终不能使用独立的视图对象,当使用VS2010 BETA2开发时,HTTP 500错误有时会发生有时又不会发生,实在令人费解。

       我想,这是不是ASP.NET MVC2当前版本的一个BUG

也许等到VS2010正式版时,ASP.NET MVC开发小组的工程师会解决这一问题。

 

 

 

 

我的CSDN博客:http://blog.csdn.net/bitfan我的新浪微博:http://t.sina.com.cn/jinxuliang