web-dev-qa-db-ja.com

ああ! 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)
30
Chaddeus

ここcodeplex の問題であり、そのエラーが発生する理由を説明しています。

http://web.archive.org/web/20131004122626/http://aspnet.codeplex.com/workitem/1795 からの引用:元のリンクが無効であるため:

HandleError属性は、ViewDataに例外情報を格納するべきではありません

HandleError属性が例外を処理すると、例外情報がViewDataに格納されます。これは、Error.aspxsite.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は変更されません。

アクションでスローされる例外は、IndexViewData(およびSiteViewData)プロパティがすでに初期化された後に発生する可能性がかなり高いです。

2010年1月27日午前12:24に閉鎖

修正されません-コメントを参照してください。


「wontfix」で言及されたコメントは、Microsoftチームの元メンバーからのものであり、それを回避するための提案(太字)が含まれています。

[HandleError]属性が実行されるまでに、元のActionResultオブジェクトへの参照が失われています。とにかくビューを表示するつもりだったかどうかはわかりません。おそらくリダイレ​​クトするつもりでした。コントローラーからビューにモデルを渡す役割を果たしていたパイプラインの一部(ViewResult)はなくなりました。

例外が発生した場合、アプリケーションが機能していたモデルは、おそらく破損しているか、使用不可として扱われるはずです。 ベストプラクティスは、エラービューとその依存関係(マスターページなど)の両方が元のモデルを必要としないようにエラービューを作成することです

17
Çağdaş Tekin

この問題に対処するための私の解決策は、レイアウトページの上部にある@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>
}
8
Gavin

私は自分のアプリで同様の問題を追跡し、修正を説明したいと思います。私の場合、次の例外が発生しました。

_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";_があり、レイアウト/ CSSインクルードを複製せずにエラーページが(サイトの他の部分と同様に)正しくスタイル設定されていることを確認しました。

_~/Views/SiteLayout.cshtml_には部分的なインクルードが含まれていました:_~/Shared/LightboxLogin.cshtml_は、ログイン用のインラインライトボックスを提供します。 _~/Shared/LightboxLogin.cshtml_には、実際のログインフォームを埋め込む部分がさらにありました。@Html.Partial("Login")には_~/Shared/Login.cshtml_が含まれています。これは、サイトのフロントエンドのログイン機能に使用されます。

エラーはサイトの管理領域で発生したため、コントローラーは「管理」であり、エラーが発生すると、_Error.cshtml_が呼び出され、_SiteLayout.cshtml_とHandleErrorInfoモデルが含まれていました。次に、これにはLightboxLoginが含まれ、次に部分的なLogin...が含まれますbut@Html.Partial("Login")に含まれる別のビューが_~/Admin/Login.cshtml_にありました代わりに。

_~/Admin/Login.cshtml_のこのビューには、_@model Web.Models.Admin.Login_がありました。

したがって、ここで学んだ教訓は、含めるパーシャルの命名に注意することです。 _~/Shared/Login.cshtml_が_~/Shared/PublicLoginForm.cshtml_であり、@Html.Partial("PublicLoginForm")が使用された場合、この問題は回避されたはずです。

サイドノート:これを次のように修正しました[ビューを再構築したくなかったため]:

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

つまり、レイアウトがエラー条件に含まれている場合、パーシャルは含まれません。

3
agrath

厳密に型指定されたビューでこのエラーが発生し、元のリクエストコンテキストのRouteData.Values ["controller"]と "action"をエラーページコントローラーとアクション名に一致するように設定することで修正しました。

ここを見ると、JSONサポートに加えて、拡張されたHandleErrorAttribute実装が表示され、結果ビューを使用して基本クラスで何が行われているかがわかります。

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

ここでのViewResultの構築がMicrosoftで使用されているロジックのようなものである場合、問題はコントローラー(または元の要求から変更されているため)ではなく、新しい(エラー状態)ビューしか指定できないことです。おそらくそれが、MVCフレームワーク/ハンドラーが型付きビューと混同されている理由です。私にはバグのようです。

上記の例にはこの修正が含まれていないため、次のように編集する必要があります(最後の2行とコメントは新しい)。

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

フィルター/属性でそれを処理しない場合は、ルーティングデータを処理する最後の2行のようなものを実行するだけで済みます。 「OnError」の例の多くは、エラーコントローラーを作成してからIContoller.Executeを呼び出します。しかし、それは別の話です。

とにかく、このエラーが発生した場合は、どこでエラーを処理していても、元の「コントローラー」と「アクション」の名前を使用している名前にリセットするだけで修正されます。

1
Tony Wall