Фильтры результатов

145

Фильтры результатов - это универсальные фильтры, которые оперируют на результатах, выдаваемых методами действий. Фильтры результатов реализуют интерфейс IResultFilter:

namespace System.Web.Mvc
{
    public interface IResultFilter
    {
        void OnResultExecuting(ResultExecutingContext filterContext);
        void OnResultExecuted(ResultExecutedContext filterContext);
    }
}

В статье Генерация ответа из контроллеров объяснялось, что методы действий возвращают результаты действий, позволяя отделять намерение метода действия от его выполнения. Когда к методу действия применяется фильтр результата, метод OnResultExecuting() будет вызван, как только метод действия возвратит результат действия, но перед выполнением этого результата. Метод OnResultExecuted() вызывается после выполнения результата действия.

В качестве параметров этим методам передаются экземпляры классов ResultExecutingContext и ResultExecutedContext соответственно, и они очень похожи на свои аналоги в фильтрах действий. В них определены те же самые свойства, обеспечивающие аналогичный эффект (смотрите предыдущую статью). Для демонстрации работы простого фильтра результата в папке Infrastructure создан новый файл класса по имени ProfileResultAttribute.cs с определением класса, приведенным в примере ниже:

using System.Diagnostics;
using System.Web.Mvc;

namespace Filter.Infrastructure
{
    public class ProfileResultAttribute : FilterAttribute, IResultFilter
    {
        private Stopwatch timer;

        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            timer = Stopwatch.StartNew();
        }

        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            timer.Stop();
            filterContext.HttpContext.Response.Write(
                    string.Format("<div>Время обработки результата: {0:F6}</div>",
                        timer.Elapsed.TotalSeconds));
        }
    }
}

Этот фильтр результата дополняет фильтр действия, созданный в предыдущей статье и позволяет измерять время, затраченное на выполнение результата. В примере ниже показано, как применить этот новый фильтр к контроллеру Home:

using System;
using System.Web.Mvc;
using Filter.Infrastructure;

namespace Filter.Controllers
{
    public class HomeController : Controller
    {
        // ...

        [ProfileAction]
        [ProfileResult]
        public string FilterTest()
        {
            return "Это метод действия FilterTest в контроллере Home";
        }
    }
}

На рисунке ниже можно видеть результат запуска приложения и перехода на URL вида /Home/FilterTest. Как видите, оба фильтра добавили данные в ответ, отправленный браузеру. Естественно, вывод из фильтра результата отображается после результата метода действия, поскольку метод OnResultExecuted() не может быть выполнен ASP.NET MVC Framework до тех пор, пока результат не будет надлежащим образом получен, что в рассматриваемом случае означает вставку в результат значения string.

Эффект от применения фильтра результата

Использование встроенного класса для фильтров действия и результата

Инфраструктура MVC Framework включает встроенный класс, позволяющий создавать производный класс, который является одновременно фильтром действия и фильтром результата. Этот класс называется ActionFilterAttribute и представлен в примере ниже:

namespace System.Web.Mvc
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, 
        Inherited = true, AllowMultiple = false)]
    public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
    {
        public virtual void OnActionExecuting(ActionExecutingContext filterContext)
        { }

        public virtual void OnActionExecuted(ActionExecutedContext filterContext)
        { }

        public virtual void OnResultExecuting(ResultExecutingContext filterContext)
        { }

        public virtual void OnResultExecuted(ResultExecutedContext filterContext)
        { }
    }
}

Единственное выгода от применения этого класса состоит в том, что он не требует переопределения и реализации методов, которые не планируется использовать - в противном случае не было бы никаких преимуществ перед непосредственной реализацией интерфейсов фильтров.

Для демонстрации использования класса ActionFilterAttribute в папку Infrastructure примера проекта добавлен новый файл класса по имени ProfileAllAttribute.cs с определением класса, показанным в примере ниже:

using System.Diagnostics;
using System.Web.Mvc;

namespace Filter.Infrastructure
{
    public class ProfileAllAttribute : ActionFilterAttribute
    {
        private Stopwatch timer;

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            timer = Stopwatch.StartNew();
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            timer.Stop();
            filterContext.HttpContext.Response.Write(
                    string.Format("<div>Общее время: {0:F6}</div>",
                        timer.Elapsed.TotalSeconds));
        }
    }
}

Класс ActionFilterAttribute реализует интерфейсы IActionFilter и IResultFilter, а это означает что MVC Framework будет воспринимать производные классы как относящиеся к обоим типам фильтров, даже если переопределены не все исходные методы. В рассматриваемом примере реализован только метод OnActionExecuting() из интерфейса IActionFilter и метод OnResultExecuted() из интерфейса IResultFilter. Это позволяет продолжить тему профилирования и провести измерение времени, требуемого для выполнения метода действия и обработки результата в виде единой задачи.

В примере ниже показано, как применить этот фильтр к контроллеру Home:

using System;
using System.Web.Mvc;
using Filter.Infrastructure;

namespace Filter.Controllers
{
    public class HomeController : Controller
    {
        // ...

        [ProfileAction]
        [ProfileResult]
        [ProfileAll]
        public string FilterTest()
        {
            return "Это метод действия FilterTest в контроллере Home";
        }
    }
}

Запустив приложение и перейдя на URL вида /Home/FilterTest, можно получить результат работы всех этих фильтров:

Эффект от добавления фильтра ProfileAll
Пройди тесты
Лучший чат для C# программистов