18

共有 _Layout.cshtml ファイル内のすべてのビューで使用するために、いくつかの共通情報を ViewBag に配置する役割を担うアクション フィルターがあります。

public class ProductInfoFilterAttribute : ActionFilterAttribute
{
    public override void
    OnActionExecuting(ActionExecutingContext filterContext)
    {
        //  build product info
        //  ... (code omitted)

        dynamic viewBag = filterContext.Controller.ViewBag;
        viewBag.ProductInfo = info;
    }
}

共有の _Layout.cshtml ファイルでは、ViewBag に入れられた情報を使用します。

...
@ViewBag.ProductInfo.Name
...

コントローラー アクションの処理中に例外が発生した場合、標準の HandleErrorAttribute は共有の Error.cshtml ビューを表示する必要があります。これは、上記のアクション フィルターを導入して _Layout.cshtml で ViewBag からの新しい値の使用を開始する前に機能していました。これで、カスタムの Error.cshtml ビューではなく、標準の ASP.Net ランタイム エラー ページが表示されます。

エラー ビューのレンダリング中に、_Layout.cshtml で ViewBag.ProductInfo.Name を使用すると、RuntimeBinderException (「null 参照でランタイム バインディングを実行できません」) がスローされるという事実までこれを追跡しました。

元の例外がスローされる前にアクション フィルターが ViewBag の値を正常に設定したにもかかわらず、Error.cshtml ビューをレンダリングするときに空の ViewBag を持つ新しいコンテキストが使用されているようです。

アクション フィルターによって作成されたデータをカスタム エラー ビューで使用できるようにする方法はありますか?

4

1 に答える 1

12

別のフィルターを追加することで、独自のソリューションを考え出しました。

public class PreserveViewDataOnExceptionFilter : IExceptionFilter
{
    public void
    OnException(ExceptionContext filterContext)
    {
        //  copy view data contents from controller to result view
        ViewResult viewResult = filterContext.Result as ViewResult;
        if ( viewResult != null )
        {
            foreach ( var value in filterContext.Controller.ViewData )
            {
                if ( ! viewResult.ViewData.ContainsKey(value.Key) )
                {
                    viewResult.ViewData[value.Key] = value.Value;
                }
            }
        }
    }

    public static void
    Register()
    {
        FilterProviders.Providers.Add(new FilterProvider());
    }

    private class FilterProvider : IFilterProvider
    {
        public IEnumerable<Filter>
        GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            //  attach filter as "first" for all controllers / actions; note: exception filters run in reverse order
            //  so this really causes the filter to be the last filter to execute
            yield return new Filter(new PreserveViewDataOnExceptionFilter(), FilterScope.First, null);
        }
    }
}

Application_Start()このフィルターは、 を呼び出して Global.asax.cs メソッドにグローバルにフックする必要がありますPreserveViewDataOnExceptionFilter.Register()

ここで行ったのは、HandleErrorAttribute フィルターの実行後に最後に実行される新しい例外フィルターをセットアップし、例外をスローしたコントローラーが使用できる ViewData コレクションの内容を、HandleErrorAttribute フィルターによって作成された結果にコピーすることです。 .

于 2011-07-06T23:30:15.603 に答える