如何在Error.cshtml视图中使用过滤器放入ViewBag的数据?

GBe*_*gen 18 asp.net-mvc action-filter viewbag asp.net-mvc-3

我有一个动作过滤器,负责将一些常见信息放入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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在共享的_Layout.cshtml文件中,我使用已放入ViewBag的信息.

...
@ViewBag.ProductInfo.Name
...
Run Code Online (Sandbox Code Playgroud)

如果处理控制器动作时发生异常,标准HandleErrorAttribute应显示我的共享Error.cshtml视图,并且这个工作之前予介绍上面的动作过滤器,并使用在_Layout.cshtml从ViewBag新的值开始.现在我得到的是标准的ASP.Net运行时错误页面,而不是我的自定义Error.cshtml视图.

我已经跟踪下来的事实,在渲染的错误观点,一个RuntimeBinderException("不能执行运行时的空引用结合")被抛出在_Layout.cshtml使用ViewBag.ProductInfo.Name的.

即使我的动作过滤器在抛出原始异常之前已在ViewBag中成功设置了值,但在渲染我的Error.cshtml视图时,仍会使用带有空ViewBag的新上下文.

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

GBe*_*gen 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

需要Application_Start()通过调用将此过滤器全局挂接到Global.asax.cs 方法中PreserveViewDataOnExceptionFilter.Register().

我在这里做的是设置一个新的异常过滤器,它在HandleErrorAttribute过滤器运行之后运行到最后,并复制可用于控件的ViewData集合的内容,该控件将异常抛出到HandleErrorAttribute过滤器创建的结果中.