C# 啊!为什么 System.Web.Mvc.HandleErrorInfo 会传递给我的视图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1997396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 22:31:26  来源:igfitidea点击:

Argh! Why does System.Web.Mvc.HandleErrorInfo get passed to my views?

c#asp.net-mvccontrollerviewsviewdata

提问by Chaddeus

I'm experiencing a rather frustrating problem. My MVC site runs fine for the most part, but randomly throws an error (which shows a friendly error to the user). When I check the logs, this is what I get:

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

Moments later, the same user could hit refresh and the page loads fine. I'm stuck. ;(

片刻之后,同一用户可以点击刷新并且页面加载正常。我被困住了。;(

Update: added stack trace

更新:添加了堆栈跟踪

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)

采纳答案by ?a?da? Tekin

Hereis an issue on codeplexexplaining why that error occurs.

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

Quote from http://web.archive.org/web/20131004122626/http://aspnet.codeplex.com/workitem/1795since original link is dead:

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

HandleError Attribute should not store exception information in ViewData

When the HandleErrorattribute handles an exception, it stores the exception information in the ViewData. This is a problem when the Error.aspxinherits from the site.masterand the site.masterclass is declared as follows.

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

SiteViewDatacontains:

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

Each page ViewDataclass inherits from the SiteViewDataclass and looks something like this

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

This approach allows one to write code in the Site.Masterpage as follows

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

Unfortunately, when an exception is thrown, the model has been replaced with an instance of the HandleErrorInfoclass. This causes an InvalidOperationExceptionto be thrown with the information

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

Is it possible for a new ErrorDataproperty to be added to the ViewResultclass to store the instance of the HandleErrorInfoclass instead? This way the ViewDatadoes not get changed.

Chances are pretty good that any exception thrown in the action will occur after the IndexViewData(and SiteViewData) properties have already been initialized.

Closed Jan 27, 2010 at 12:24 AM by

Won't fix - see comments.

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

HandleError属性处理异常时,它将异常信息存储在ViewData. 当Error.aspx继承自site.mastersite.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抛出信息

传递到字典中的模型项是 type ,System.Web.Mvc.HandleErrorInfo但是这个字典需要一个 type 的模型项Igwt.Boh.Website.Web.Controllers.SiteViewData

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

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

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

不会修复 - 见评论。



The comments mentioned with "wontfix" are from a former member of the Microsoft team, along with their suggestion for working around it (bolded):

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

By the time the [HandleError] attribute executes, we've lost the reference to the original ActionResult object. We don't even know if you intended to show a view anyway - maybe you intended to redirect. The part of the pipeline (the ViewResult) that would have been responsible for passing the model from the controller to the view is gone.

If an exception occurs, any model the application was working on should probably be treated as corrupt or unavailable anyway. The best practice would be to write your Error view such that neither it nor its dependencies (such as its master page) requires the original model.

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

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

回答by Tony Wall

I had this error with strongly typed views and fixed it by also setting the original request context's RouteData.Values["controller"] and "action" to match the error page controller and action names.

我在强类型视图中遇到了这个错误,并通过设置原始请求上下文的 RouteData.Values["controller"] 和 "action" 来匹配错误页面控制器和操作名称来修复它。

If you look here you will see an enhanced HandleErrorAttribute implementation which besides the JSON support also shows you what is going on in the base class with the result view.

如果您查看此处,您将看到一个增强的 HandleErrorAttribute 实现,除了 JSON 支持之外,它还向您展示了带有结果视图的基类中发生了什么。

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

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

If the ViewResult construction here is anything like the logic used by Microsoft, then the issue could be that it is only able to specify a new (error condition) view, not the controller or action (as it has changed from the original request). Perhaps that's why the MVC framework/handlers are getting confused with typed views. It seems like a bug to me.

如果此处的 ViewResult 构造与 Microsoft 使用的逻辑类似,那么问题可能在于它只能指定新的(错误条件)视图,而不是控制器或操作(因为它已从原始请求更改)。也许这就是 MVC 框架/处理程序与类型化视图混淆的原因。这对我来说似乎是一个错误。

The example above does NOT include this fix, so you'll have to edit it as follows (last two lines and comment are new):

上面的示例不包含此修复程序,因此您必须按如下方式对其进行编辑(最后两行和注释是新的):

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";

If you're not handling it in a filter/attribute then you only need do something like the last two lines where you are dealing with the routing data, e.g. many "OnError" examples construct an error controller then call IContoller.Execute. But that's another story.

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

Anyway, if you get this error, wherever you are handling the error, just reset the original "controller" and "action" name to whatever you are using and it may fix it for you too.

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

回答by agrath

I've just tracked down a similar issue in my app and wanted to describe the fix for me. In my case, I was getting the following 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'.

And I was using [HandleError]to route errors to ~/Shared/Error.cshtml

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

What happened [at least in my case] was: ~/Shared/Error.cshtmlhad Layout = "~/Views/SiteLayout.cshtml";to ensure that the Error page was styled correctly (like the rest of the site) without duplicating the layout/css includes.

发生的事情 [至少在我的情况下] 是: ~/Shared/Error.cshtml必须Layout = "~/Views/SiteLayout.cshtml";确保错误页面的样式正确(与网站的其余部分一样),而不复制布局/css 包含。

~/Views/SiteLayout.cshtmlhad a partial included: ~/Shared/LightboxLogin.cshtmlwhich provides an inline lightbox for the login. ~/Shared/LightboxLogin.cshtmlhad a further partial to embed the actual login form: @Html.Partial("Login")which includes ~/Shared/Login.cshtmlThis is used for the login functionality on the front-end of the site.

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

Because the error was caused in the Admin area of the site, the controller was "Admin" and when an error occurred, Error.cshtmlwas invoked, which included SiteLayout.cshtmlwith a HandleErrorInfomodel. This in turn included LightboxLogin, which then included the Partial, Login... butthere was another view in ~/Admin/Login.cshtmlwhich was included by the @Html.Partial("Login")instead.

因为错误是在站点的管理区域引起的,所以控制器是“Admin”,当错误发生时,它Error.cshtml被调用,它包含SiteLayout.cshtml在一个HandleErrorInfo模型中。这反过来包括LightboxLogin,然后包括部分,Login......但是还有另一种观点~/Admin/Login.cshtml@Html.Partial("Login")替代。

This view at ~/Admin/Login.cshtmlhad this: @model Web.Models.Admin.Login

这个观点~/Admin/Login.cshtml有这个:@model Web.Models.Admin.Login

So, lesson learned here is be careful of your naming of your partials that you want to include. If ~/Shared/Login.cshtmlwas ~/Shared/PublicLoginForm.cshtmland @Html.Partial("PublicLoginForm")was used then this issue would have been avoided.

所以,这里学到的教训是小心你要包含的部分的命名。如果~/Shared/Login.cshtml~/Shared/PublicLoginForm.cshtml@Html.Partial("PublicLoginForm")被使用,那么就会被避免了这个问题。

Sidenote: I fixed this like so [as I didn't want to restructure my views]:

旁注:我是这样修复的[因为我不想重组我的观点]:

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

Which means the Partial is not included when the layout is included in an Error condition.

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

回答by Gavin

My solution for dealing with the problem is to remove the @model directive at the top of the layout page and then do some checks where I'd normally expect to see my model to switch between the different models that might get passed in e.g.

我处理这个问题的解决方案是删除布局页面顶部的 @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>
}