Asp.net-Mvc

如何使用 Error.cshtml 視圖中的過濾器放入 ViewBag 的數據?

  • July 6, 2011

我有一個動作過濾器,負責將一些通用資訊放入 ViewBag 中,以供共享 _Layout.cshtml 文件中的所有視圖使用。

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 的新值之前有效。現在我得到的是標準的 ASP.Net 執行時錯誤頁面,而不是我的自定義 Error.cshtml 視圖。

我已將此歸結為以下事實:在呈現錯誤視圖時,在 _Layout.cshtml 中使用 ViewBag.ProductInfo.Name 時會引發 RuntimeBinderException(“無法對空引用執行執行時綁定”)。

看起來即使我的操作過濾器在引發原始異常之前已成功設置 ViewBag 中的值,但在呈現我的 Error.cshtml 視圖時仍使用具有空 ViewBag 的新上下文。

有什麼方法可以讓操作過濾器創建的數據可用於自定義錯誤視圖?

我通過添加另一個過濾器提出了自己的解決方案。

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 過濾器創建的結果的控制器.

引用自:https://stackoverflow.com/questions/6602638