Подтвердить что ты не робот

Как я могу использовать данные, помещенные в ViewBag фильтром в моем представлении Error.cshtml?

У меня есть фильтр действий, который отвечает за размещение некоторой общей информации в 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, и это сработало до того, как я представил фильтр действий выше и начал использовать новые значения из ViewBag в _Layout.cshtml. Теперь то, что я получаю, это стандартная страница ошибки выполнения ASP.Net вместо моего пользовательского представления Error.cshtml.

Я проследил это до того, что при визуализации представления ошибки исключение RuntimeBinderException ( "Невозможно выполнить привязку времени выполнения по нулевой ссылке" ) бросается на использование ViewBag.ProductInfo.Name в _Layout.cshtml.

Похоже, что хотя мой фильтр действий успешно установил значение в ViewBag до того, как было отправлено исходное исключение, новый контекст с пустой ViewBag используется при визуализации моего представления Error.cshtml.

Есть ли способ получить данные, созданные фильтром действий, для доступа к пользовательскому представлению об ошибках?

4b9b3361

Ответ 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.