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)
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 leViewData
. C'est un problème lorsqueError.aspx
hérite de la classesite.master
et la classesite.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 classeSiteViewData
et ressemble à cecipublic 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 unInvalidOperationException
à lancer avec les informationsL'é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 typeIgwt.Boh.Website.Web.Controllers.SiteViewData
.Est-il possible qu'une nouvelle propriété
ErrorData
soit ajoutée à la classeViewResult
pour stocker l'instance de la classeHandleErrorInfo
à la place? De cette façon, leViewData
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
(etSiteViewData
) 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.
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>
}
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.
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.