Подтвердить что ты не робот

Argh! Почему 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)
4b9b3361

Ответ 1

Здесь есть проблема на codeplex объяснение причины возникновения этой ошибки.

Цитата из http://web.archive.org/web/20131004122626/http://aspnet.codeplex.com/workitem/1795, поскольку исходная ссылка мертва:

Атрибут HandleError не должен хранить информацию об исключении в ViewData​​h1 >

Когда атрибут 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 с информацией

Элемент модели, переданный в словарь, имеет тип System.Web.Mvc.HandleErrorInfo, но для этого словаря требуется элемент модели типа Igwt.Boh.Website.Web.Controllers.SiteViewData.

Возможно ли добавить новое свойство ErrorData в класс ViewResult для хранения экземпляра класса HandleErrorInfo? Таким образом, ViewData не изменяется.

Скорее всего, любое исключение, вызванное действием, произойдет после того, как свойства IndexViewDataSiteViewData) уже были инициализированы.

Закрыто 27 января 2010 года в 12:24 по

Не будет исправлено - см. комментарии.


Комментарии, упомянутые в "wontfix", принадлежат бывшему члену команды Microsoft, а также предложению обойти его (bolded):

К тому времени, когда выполняется атрибут [HandleError], мы потеряли ссылку на исходный объект ActionResult. Мы даже не знаем, если вы намеревались показать представление в любом случае - возможно, вы намеревались перенаправить. Часть трубопровода (ViewResult), которая была бы Ответственный за передачу модели с контроллера на представление прошло.

Если возникает исключение, любая модель, над которой работало приложение в любом случае, вероятно, следует рассматривать как коррумпированные или недоступные. Лучший практика заключалась бы в том, чтобы написать ваше представление об ошибке, чтобы ни оно, ни его зависимостям (например, главной странице) требуется оригинал модель.

Ответ 2

Мое решение для решения проблемы состоит в том, чтобы удалить директиву @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>
}

Ответ 3

Я только что просмотрел подобную проблему в своем приложении и хотел описать это исправление для меня. В моем случае я получил следующее исключение:

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.cshtml имел Layout = "~/Views/SiteLayout.cshtml";, чтобы убедиться, что страница ошибки была написана правильно (например, остальная часть сайта) без дублирования layout/css.

~/Views/SiteLayout.cshtml был включен частичный: ~/Shared/LightboxLogin.cshtml, который предоставляет встроенный лайтбокс для входа. ~/Shared/LightboxLogin.cshtml имел еще частичную возможность вставить фактическую форму входа: @Html.Partial("Login"), которая включает ~/Shared/Login.cshtml Это используется для функций входа в систему на переднем конце сайта.

Поскольку ошибка была вызвана в области администратора сайта, контроллер был "Admin", и когда произошла ошибка, был вызван Error.cshtml, который включал SiteLayout.cshtml с моделью 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"), тогда этой проблемы можно было бы избежать.

Sidenote: я исправил это так, чтобы [как я не хотел реструктурировать свои взгляды]:

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

Это означает, что Partial не включается, когда макет включен в состояние ошибки.

Ответ 4

У меня была эта ошибка с строго типизированными представлениями и исправлена ​​ее, также устанавливая исходный контекст запроса RouteData.Values ​​[ "controller" ] и "action" для соответствия контроллеру страницы ошибок и именам действий.

Если вы посмотрите здесь, вы увидите расширенную реализацию HandleErrorAttribute, которая помимо поддержки JSON также покажет вам, что происходит в базовом классе с представлением результата.

http://www.dotnet-tricks.com/Tutorial/mvc/19D9140313-Exception-or-Error-Handling-and-Logging-in-MVC4.html

Если конструкция 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. Но эта другая история.

В любом случае, если вы получите эту ошибку, везде, где вы обрабатываете ошибку, просто reset исходное имя "controller" и "action" для всего, что вы используете, и оно также может исправить это для вас.