Argh! Pourquoi ne Système de.Web.Mvc.HandleErrorInfo est passé à mes vues?

Je rencontre un problème plutôt frustrant. Mon site MVC fonctionne très bien pour la plupart, mais lance aléatoirement une erreur (ce qui montre une erreur amicale à l'utilisateur). Quand je vérifie les journaux, voici ce que je reçois:

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'.

Quelques instants plus tard, le même utilisateur pourrait appuyer sur Actualiser et la page se charge correctement. Je suis coincé. ;(

Mise à jour: Ajout d'une trace de pile

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)
25
demandé sur Chaddeus 2010-01-04 06:40:13

4 réponses

Ici est une question sur codeplex expliquant pourquoi cette erreur se produit.

Citation de http://web.archive.org/web/20131004122626/http://aspnet.codeplex.com/workitem/1795 puisque le lien d'origine est mort:

L'attribut HandleError ne doit pas stocker les informations d'exception dans ViewData

Lorsque le HandleError attribut gère une exception, il stocke les informations d'exception dans le ViewData. C'est un problème lorsque Error.aspx hérite de la classe site.master et la classe site.master sont déclarées comme suit.

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

SiteViewData contient:

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

Chaque page ViewData classe hérite de la classe SiteViewData et ressemble à ceci

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

Cette approche permet d'écrire du code dans la page Site.Master comme suit

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

Malheureusement, lorsqu'une exception est levée, le modèle a été remplacé par une instance de la HandleErrorInfo classe. Cela provoque un InvalidOperationException à lancer avec les informations

L'élément de modèle adopté dans le dictionnaire est de type System.Web.Mvc.HandleErrorInfo, mais ce dictionnaire nécessite un élément de modèle de type Igwt.Boh.Website.Web.Controllers.SiteViewData.

Est-il possible qu'une nouvelle propriété ErrorData soit ajoutée à la classe ViewResult pour stocker l'instance de la classe HandleErrorInfo à la place? De cette façon, le ViewData ne change pas.

Les Chances sont assez bonnes que toute exception levée dans l'action se produise après que les propriétés IndexViewData (et SiteViewData) ont déjà été initialisées.

Fermé Jan 27, 2010 à 12: 24 par

Ne corrigera pas-voir les commentaires.


Les commentaires mentionnés avec "wontfix" proviennent d'un ancien membre de L'équipe Microsoft, ainsi que leur suggestion de contourner le problème (en gras):

Au moment où l'attribut [HandleError] s'exécute, nous avons perdu référence à L'objet ActionResult d'origine. Nous ne savons même pas si vous aviez l'intention d'afficher une vue de toute façon-peut - être que vous aviez l'intention de rediriger. La partie de la canalisation (le ViewResult) qui aurait été responsable du passage du modèle du contrôleur à la vue est disparu.

Si une exception se produit, tout modèle sur lequel l'application travaillait devrait probablement être traité comme corrompu ou indisponible de toute façon. LE MEILLEUR la pratique serait d'écrire votre vue D'erreur de telle sorte que ni elle ni ses dépendances (telles que sa page maître) nécessitent l'original modèle.

15
répondu Çağdaş Tekin 2017-06-07 10:59:19

Ma solution pour résoudre le problème est de supprimer la directive @ model en haut de la page de mise en page, puis de faire quelques vérifications où je m'attendrais normalement à voir mon modèle basculer entre les différents modèles qui pourraient être transmis par exemple

@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>
}
6
répondu Gavin 2015-05-28 23:11:41

Je viens de retrouvé un problème similaire dans mon application et je voulais décrire le correctif pour moi. Dans mon cas, j'obtenais l'exception suivante:

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'.

Et j'utilisais [HandleError] pour acheminer les erreurs vers ~/Shared/Error.cshtml

Ce qui s'est passé [au moins dans mon cas] était: ~/Shared/Error.cshtml avait Layout = "~/Views/SiteLayout.cshtml"; pour s'assurer que la page D'erreur était correctement stylée (comme le reste du site) sans dupliquer la mise en page/CSS inclut.

~/Views/SiteLayout.cshtml avait un partiel inclus: ~/Shared/LightboxLogin.cshtml, qui fournit une lightbox en ligne pour la connexion. ~/Shared/LightboxLogin.cshtml avait un autre partiel pour intégrer le formulaire de connexion réel: @Html.Partial("Login") qui inclut ~/Shared/Login.cshtml Ceci est utilisé pour la fonctionnalité de connexion sur le front-end du site.

Parce que l'erreur a été causée dans la zone D'administration du site, le contrôleur était "Admin" et lorsqu'une erreur s'est produite, Error.cshtml a été invoqué, qui comprenait SiteLayout.cshtml avec un modèle HandleErrorInfo. Ceci à son tour inclus LightboxLogin, qui a ensuite Inclus Le partiel, Login... mais Il y avait une autre vue dans ~/Admin/Login.cshtml, qui a été inclus par le @Html.Partial("Login") à la place.

Ce point de vue à ~/Admin/Login.cshtml avait ceci: @model Web.Models.Admin.Login

Donc, la leçon apprise ici est de faire attention à la dénomination de vos partiels que vous voulez inclure. Si ~/Shared/Login.cshtml était ~/Shared/PublicLoginForm.cshtml et que @Html.Partial("PublicLoginForm") était utilisé, ce problème aurait été évité.

Sidenote: j'ai corrigé ceci comme ceci [car je ne voulais pas restructurer mes vues]:

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

Ce qui signifie que le partiel n'est pas inclus lorsque la mise en page est incluse dans une condition D'erreur.

3
répondu agrath 2014-12-11 20:47:11

J'ai eu cette erreur avec des vues fortement typées et l'ai corrigée en définissant également RouteData du contexte de requête d'origine.Valeurs ["controller"] et "action" pour correspondre au contrôleur de page d'erreur et aux noms d'action.

Si vous regardez ici, vous verrez une implémentation handleerrorattribute améliorée qui, outre le support JSON, vous montre également ce qui se passe dans la classe de base avec le résultat vue.

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

Si la construction ViewResult ici est quelque chose comme la logique utilisée par Microsoft, alors le problème pourrait être qu'il est seulement capable de spécifier une nouvelle vue (condition d'erreur), pas le contrôleur ou l'action (car il a changé de la requête d'origine). C'est peut-être pourquoi le framework/gestionnaires MVC est confondu avec les vues typées. Il semble comme un bug pour moi.

Le l'exemple ci-dessus n'inclut pas ce correctif, vous devrez donc le modifier comme suit (les deux dernières lignes et le commentaire sont nouveaux):

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

Si vous ne le gérez pas dans un filtre/attribut, il vous suffit de faire quelque chose comme les deux dernières lignes où vous traitez les données de routage, par exemple de nombreux exemples "OnError" construisent un contrôleur d'erreur puis appellent IContoller.Exécuter. Mais c'est une autre histoire.

Quoi qu'il en soit, si vous obtenez cette erreur, où que vous manipuliez l'erreur, réinitialisez simplement le nom original "contrôleur " et" action " à tout ce que vous utilisez et il peut le réparer pour vous aussi.

1
répondu Tony Wall 2018-05-12 10:42:30