哎呀! 为什么System.Web.Mvc.HandleErrorInfo会传递给我的视图?

我遇到了一个相当令人沮丧的问题。 我的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) 

这是codeplex上的一个问题,解释了为什么会发生错误。

引自http://web.archive.org/web/20131004122626/http://aspnet.codeplex.com/workitem/1795,因为原始链接已死:

HandleError属性不应在ViewData中存储exception信息

HandleError属性处理exception时,它会将exception信息存储在ViewData 。 当Error.aspxinheritance自site.master并且site.master类声明如下时,这是一个问题。

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

SiteViewData包含:

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

每个页面ViewData类都inheritance自SiteViewData类,看起来像这样

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

这种方法允许用户在Site.Master页面中编写代码,如下所示

 <%= Html.Encode(ViewData.Model.Title) %> 

不幸的是,当抛出exception时,模型已被HandleErrorInfo类的实例替换。 这会导致InvalidOperationException与信息一起抛出

传递到字典中的模型项的类型为System.Web.Mvc.HandleErrorInfo但此字典需要Igwt.Boh.Website.Web.Controllers.SiteViewData类型的模型项。

是否可以将新的ErrorData属性添加到ViewResult类以存储HandleErrorInfo类的实例? 这样ViewData就不会改变。

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

关闭2010年1月27日上午12:24 by

无法修复 – 请参阅评论。


“wontfix”中提到的评论来自微软团队的前成员,以及他们解决它的建议(粗体):

到[HandleError]属性执行时,我们已经丢失了对原始ActionResult对象的引用。 我们甚至不知道您是否打算展示视图 – 也许您打算重定向。 管道中的部分(ViewResult)负责将模型从控制器传递到视图。

如果发生exception,应用程序正在处理的任何模型都应该被视为已损坏或无法使用。 最佳做法是编写错误视图,使其及其依赖关系(例如其母版页)都不需要原始模型。

我解决这个问题的方法是删除布局页面顶部的@model指令,然后做一些检查,我通常希望看到我的模型在可能传入的不同模型之间切换,例如

 @if (Model is System.Web.Mvc.HandleErrorInfo) { Error } else if (Model.GetType() == typeof(MyApp.Models.BaseViewModel)) {  @Model.PageTitleComplete } 

我刚刚在我的应用程序中找到了类似的问题,并希望为我描述修复程序。 在我的情况下,我得到以下exception:

 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这用于前端的登录function现场。

因为错误是在站点的Admin区域中引起的,所以控制器是“Admin”,当发生错误时,调用了HandleErrorInfo ,其中包括带有HandleErrorInfo模型的HandleErrorInfo 。 这反过来包括LightboxLogin ,然后包括Partial, 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") } 

这意味着当布局包含在错误条件中时,不包括部分。

我在强类型视图中遇到此错误并通过设置原始请求上下文的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, eg to prevent error with typed views filterContext.RouteData.Values["controller"] = "MyError"; // MyErrorController.Index(HandleErrorInfo) filterContext.RouteData.Values["action"] = "Index"; 

如果您没有在filter/属性中处理它,那么您只需要执行类似于处理路由数据的最后两行,例如,许多“OnError”示例构造一个错误控制器,然后调用IContoller.Execute。 但这是另一个故事。

无论如何,如果您收到此错误,无论您在何处处理错误,只需将原始“控制器”和“操作”名称重置为您正在使用的任何名称,它也可以为您修复它。