【问题标题】:How can I use data placed into a ViewBag by a filter in my Error.cshtml view?如何使用 Error.cshtml 视图中的过滤器放入 ViewBag 的数据?
【发布时间】:2011-07-06 20:37:36
【问题描述】:

我有一个动作过滤器,负责将一些通用信息放入 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 的新上下文。

有没有办法让操作过滤器创建的数据可用于自定义错误视图?

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-3 action-filter viewbag


    【解决方案1】:

    我通过添加另一个过滤器提出了自己的解决方案。

    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);
            }
        }
    }
    

    这个过滤器需要在 Global.asax.cs Application_Start() 方法中通过调用PreserveViewDataOnExceptionFilter.Register() 全局挂钩。

    我在这里所做的是设置一个新的异常过滤器,该过滤器在 HandleErrorAttribute 过滤器运行之后最后运行,并将 ViewData 集合的内容复制给将异常抛出到由创建的结果的控制器HandleErrorAttribute 过滤器。

    【讨论】:

    • 这对 ViewData 非常有效,我喜欢这个代码,但是你有 ViewBag 的等价物吗?
    • filterContext.Controller.ViewData 就我所见包含模型和 ViewBag,所以可以全部传递,或者只传递你需要的位
    猜你喜欢
    • 1970-01-01
    • 2012-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多