啊!为什么System.Web.Mvc.HandleErrorInfo会传递到我的视图中?

33

我遇到了一个非常令人沮丧的问题。我的MVC网站大部分时间都能正常运行,但是随机地会抛出一个错误(向用户显示友好的错误)。当我检查日志时,得到的信息如下:

System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Web.Mvc.HandleErrorInfo' but this dictionary requires a model item of type 'BaseViewData'.

过了一会儿,同一用户可以点击刷新,页面就可以正常加载。我卡住了。 ;(

更新:添加堆栈跟踪

System.Web.HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Web.Mvc.HandleErrorInfo' but this dictionary requires a model item of type 'BaseViewData'.
   at System.Web.Mvc.ViewDataDictionary`1.SetModel(Object value)
   at System.Web.Mvc.ViewDataDictionary..ctor(ViewDataDictionary dictionary)
   at System.Web.Mvc.HtmlHelper`1..ctor(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection)
   at System.Web.Mvc.ViewMasterPage`1.get_Html()
   at ASP.views_shared_site_master.__Render__control1(HtmlTextWriter __w, Control parameterContainer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Control.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Page.Render(HtmlTextWriter writer)
   at System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   --- End of inner exception stack trace ---
   at System.Web.UI.Page.HandleError(Exception e)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest()
   at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
   at System.Web.UI.Page.ProcessRequest(HttpContext context)
   at ASP.views_shared_error_aspx.ProcessRequest(HttpContext context)
   at System.Web.Mvc.ViewPage.RenderView(ViewContext viewContext)
   at System.Web.Mvc.WebFormView.RenderViewPage(ViewContext context, ViewPage page)
   at System.Web.Mvc.WebFormView.Render(ViewContext viewContext, TextWriter writer)
   at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
   at System.Web.Mvc.Controller.ExecuteCore()
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
   at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext)
   at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
4个回答

21

这里是在codeplex上解释那个错误出现的问题。

由于原始链接已失效,以下是http://web.archive.org/web/20131004122626/http://aspnet.codeplex.com/workitem/1795的摘录:

HandleError特性不应该将异常信息存储在ViewData中

HandleError特性处理异常时,它会将异常信息存储在ViewData中。但如果Error.aspx继承自site.master,而site.master类如下所示,则会出现问题。

public partial class Site : System.Web.Mvc.ViewMasterPage<SiteViewData>
{
}

SiteViewData 包含:

public class SiteViewData 
{
  public String Title { get; set; } 
}

每个页面的ViewData类都继承自SiteViewData类,看起来像这样:

public class IndexViewData : SiteViewData
{
  public String Message { get; set; }
  public String SupportedLanguages {get; set;}
}

这种方法允许您在Site.Master页面中编写以下代码

<title><%= Html.Encode(ViewData.Model.Title) %></title>
不幸的是,当抛出异常时,模型已被替换为 HandleErrorInfo 类的实例。这会导致抛出一个 InvalidOperationException 异常,并显示以下信息:

The model item passed into the dictionary is of type System.Web.Mvc.HandleErrorInfo but this dictionary requires a model item of type Igwt.Boh.Website.Web.Controllers.SiteViewData.

是否可以向 ViewResult 类中添加一个新的 ErrorData 属性来存储 HandleErrorInfo 类的实例?这样就不会改变 ViewData 了。

很有可能,在操作中引发的任何异常都会在初始化 IndexViewData(和 SiteViewData)属性之后发生。

该问题于 2010 年 1 月 27 日关闭。

"wontfix" 的评论来自微软团队的一名前成员,他们提出了解决此问题的建议(已加粗):

当 [HandleError] 特性执行时,我们已经失去了对原始 ActionResult 对象的引用。我们甚至不知道你是否打算显示视图-也许你打算重定向。将模型从控制器传递到视图的管道部分(ViewResult)已经不存在了。

如果发生了异常,应该将应用程序正在处理的任何模型视为已损坏或不可用。最佳做法是编写您的错误视图,使其自身及其依赖项(例如其母版页)都不需要原始模型。


3
你说得对,我稍微深入了解了一下,我认为我已经找到问题所在了,出错页面源自需要<BaseViewData>的站点主页面。至少这是一个简单的修复方案,我将制作一个新的主页面来让我的错误页面继承。谢谢! - Chaddeus
System.Web.Mvc.WebFormView.RenderViewPage(ViewContext context, ViewPage page) 在 System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) 中执行,在 System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) 中调用,在 System.Web.Mvc.Controller.ExecuteCore() 中执行,在 System.Web.Mvc.MvcHandler.<>c__DisplayClass8.<BeginProcessRequest>b__4() 中执行,在 System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0() 中执行,在 System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 中执行。 - Sreedhar K
在System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)处执行步骤。 - Sreedhar K
1
@Sreedhar K,Codeplex上的问题只是描述了错误发生的原因。我相信@Chad通过创建一个主页面并声明Inherits="System.Web.Mvc.ViewMasterPage",并将该主页面用于他的错误视图来解决了这个问题。 - Çağdaş Tekin
@Sreedhar,说实话我不知道。我自己没有遇到过这个问题。从阅读那个问题的理解来看,似乎只有使用不同的主页面才是解决方法。 - Çağdaş Tekin
显示剩余2条评论

10

我解决这个问题的方法是在布局页面顶部删除@model指令,然后在通常期望看到我的模型的位置进行一些检查,以在不同的传递模型之间进行切换。

@if (Model is System.Web.Mvc.HandleErrorInfo)
{
    <title>Error</title>
}
else if (Model.GetType() == typeof(MyApp.Models.BaseViewModel))
{
    <meta name="description" content="@Model.PageMetaDescription">
    <title>@Model.PageTitleComplete</title>
}

4

我刚刚在我的应用程序中追踪到一个类似的问题,并想为我描述解决方法。 在我的情况下,我遇到了以下异常:

System.InvalidOperationException: The model item passed into the dictionary is of 
type 'System.Web.Mvc.HandleErrorInfo', but this dictionary requires a model item of
type 'Web.Models.Admin.Login'.

我曾使用[HandleError]将错误路由到~/Shared/Error.cshtml

在我的情况下,发生了什么是: ~/Shared/Error.cshtmlLayout = "~/Views/SiteLayout.cshtml";,以确保错误页面正确地被样式化(像网站的其余部分一样),而不会重复布局/CSS引入。

~/Views/SiteLayout.cshtml包含一个局部文件:~/Shared/LightboxLogin.cshtml,为登录提供内联灯箱。 ~/Shared/LightboxLogin.cshtml还有一个嵌入实际登录表单的局部文件:@Html.Partial("Login"),其中包括~/Shared/Login.cshtml 这用于站点前端的登录功能。

因为错误是在网站的管理区域中引起的,所以控制器是“Admin”,当出现错误时,将调用Error.cshtml,它将使用HandleErrorInfo模型包含SiteLayout.cshtml。 这反过来又包含LightboxLogin,然后包含局部文件Login... 但是存在另一个视图~/Admin/Login.cshtml,它被@Html.Partial("Login")包含。

这个位于~/Admin/Login.cshtml的视图有这样的内容:@model Web.Models.Admin.Login

因此,要小心您想要包含的局部文件的命名。如果~/Shared/Login.cshtml~/Shared/PublicLoginForm.cshtml,并且使用了@Html.Partial("PublicLoginForm"),则可以避免此问题。

附注:我这样修复了它[因为我不想重构我的视图]:

@if (!(Model is HandleErrorInfo))
{
   @Html.Partial("LightboxLogin")
}

这意味着当布局在错误条件下被包含时,部分内容不会被包括进去。

1
我在强类型视图中遇到了这个错误,通过将原始请求上下文的RouteData.Values["controller"]和"action"设置为与错误页面控制器和操作名称匹配来解决它。
如果你在这里看,你会看到一个增强的HandleErrorAttribute实现,除了JSON支持外,还向你展示了基类中结果视图的情况。

https://www.dotnettricks.com/learn/mvc/exception-or-error-handling-and-logging-in-mvc4

如果这里的ViewResult构造与Microsoft使用的逻辑相似,那么问题可能在于它只能指定一个新的(错误条件)视图,而不能指定控制器或操作(因为它已经从原始请求中更改)。也许这就是MVC框架/处理程序在类型化视图方面感到困惑的原因。对我来说,这似乎是一个错误。
上面的示例不包括此修复程序,因此您需要进行以下编辑(最后两行和注释是新内容):
var model = new HandleErrorInfo(httpError, controllerName, actionName);
filterContext.Result = new ViewResult
{
    ViewName = View,
    MasterName = Master,
    ViewData = new ViewDataDictionary(model),
    TempData = filterContext.Controller.TempData
};

// Correct routing data when required, e.g. to prevent error with typed views
filterContext.RouteData.Values["controller"] = "MyError";  // MyErrorController.Index(HandleErrorInfo)
filterContext.RouteData.Values["action"] = "Index";

如果您没有在过滤器/属性中处理它,那么您只需要像处理路由数据一样处理最后两行代码,例如很多“OnError”示例会构建一个错误控制器然后调用IContoller.Execute。但这是另外一个故事。
无论如何,如果您遇到此错误,请在处理错误的任何位置将原始的“controller”和“action”名称重置为您正在使用的内容,这也可能会为您解决问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接