Как я могу использовать данные, помещенные в 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 до того, как было сгенерировано исходное исключение, при отображении моего представления Error.cshtml используется новый контекст с пустым 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.,