Фильтры

Введение в фильтры

Последнее обновление: 09.04.2022

Фильтры позволяют выполнять некоторые действия до или после определенной стадии обработки запроса. В ASP.NET Core MVC имеются следующие типы фильтров:

  • Фильтры авторизации: определяют, авторизован ли пользователь для выполнения текущего запроса. Если пользователь не авторизованн для доступа к ресурсу, то фильтр завершает обработку запроса.

  • Фильтры ресурсов: выполняются после фильтров авторизации. Его метод OnResourceExecuting() выполняется до всех остальных фильтров и до привязки модели, а его метод OnResourceExecuted() выполняется после всех остальных фильтров

  • Фильтры действий: применяется только к действиям контроллера, запускается после фильтра ресурсов как до, так и после выполнения метода контроллера

  • Фильтры исключений: определяют действия в отношении необработанных исключений

  • Фильтры результатов действий: фильтр применяется к результатам методов контроллера, выполняется как до, так и после получения результата

Вместе все эти типы фильтров образуют конвейер фильтров (filter pipeline), который встроен в процесс обработки запроса в MVC и который начинает выполняется после того, как инфраструктура MVC выбрала метод контроллера для обработки запроса. На разных этапах обработки запроса в этом конвейере вызывается соответствующий фильтр:

Filter Pipeline или конвейер фильтров в ASP.NET Core MVC и C#

Определение фильтров

Несмотря на то, что конвейер фильтров образуют пять разных типов фильтров, которые вызываются на разных этапах и имеют свою строго задачу, тем не менее все они имеют общие моменты реализации. Так, все фильтры поддерживают два способа реализации: синхронную и асинхронную. В зависимости от выбранного способа класс фильтра будет реализовать тот или иной интерфейс.

Тип фильтра

Синхронный интерфейс

Асинхронный интерфейс

Фильтр авторизации

IAuthorizationFilter

IAsyncAuthorizationFilter

Фильтр ресурсов

IResourceFilter

IAsyncResourceFilter

Фильтр действий

IActionFilter

IAsyncActionFilter

Фильтр исключений

IExceptionFilter

IAsyncExceptionFilter

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

IResultFilter

IAsyncResultFilter

Все фильтры имеют одну и ту же схему. Синхронный интерфейс, который реализуют фильтры, называется I[Stage]Filter, где [Stage] - это этап обработки запроса, на котором вызывается фильтр. Например, для фильтра авторизации этап условно называется Authorization, для фильтров ресурсов - Resoure, для фильтров действий - Action, для фильтров результатов - Result, для фильтров исключений - Exception.

Синхронные фильтры (за исключением фильтра Razor Page) определяют два метода: On[Stage]Executing и On[Stage]Executed. Метод On[Stage]Executing вызывается непосредственно перед этапом Stage, а метод On[Stage]Executed сразу после завершения этапа [Stage].

Например, типовая реализация для синхронного фильтра действий:

using Microsoft.AspNetCore.Mvc.Filters;

public class SimpleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // код метода
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // код метода
    }
}

Асинхронные интерфейсы фильтров называются IAsync[Stage]Filter, и они определяют только один метод - On[Stage]ExecutionAsync

using Microsoft.AspNetCore.Mvc.Filters;

public class SimpleAsynActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, 
									ActionExecutionDelegate next)
    {
        // код метода
        await next();
    }  
}

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

К примеру, создадим свой фильтр. Для этого добавим в проект папку Filters, в которой будут храниться фильтры. И далее добавим в эту папку новый класс SimpleResourceFilter, который будет представлять простейший фильтр ресурсов

using Microsoft.AspNetCore.Mvc.Filters;

namespace MvcApp.Filters
{
    public class SimpleResourceFilter : Attribute, IResourceFilter
    {
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            context.HttpContext.Response.Cookies.Append("LastVisit", DateTime.Now.ToString("dd/MM/yyyy HH-mm-ss"));
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
			// реализация отсутствует
        }
    }
}
Определение фильтров в ASP.NET Core MVC и C#

Смысл фильтра будет заключаться в том, что он в ответ пользователю будет добавлять куки "LastVisit", которые будут хранить дату и время последнего визита.

Обратите внимание! Изменение объекта Response (то есть по сути изменение ответа пользователю) проивзодится в методе OnResourceExecuting, который выполняется до методов контроллера и соответственно до выполнения результата и формирования ответа пользователю. Если мы попытаемся выполнить все те же действия в методе OnResourceExecuted, то приложение в процессе выполнения сгенерирует исключение. Это нужно иметь в виду при реализации всех видов фильтров.

Также стоит отметить, что класс фильтра также наследует класс Attribute, так как фильтры реализуются в основном именно как атрибуты, что упрощает их применение.

Применение фильтра

Теперь применим выше определенный фильтр и для этого определим следующий код контроллера:

using Microsoft.AspNetCore.Mvc;
using MvcApp.Filters;  // пространство имен фильтра SimpleResourceFilter

namespace MvcApp.Controllers
{
    [SimpleResourceFilter]
    public class HomeController : Controller
    {
        public IActionResult Index() => View();
    }
}

В данном случае фильтр применяется ко всему контроллеру. Запустим проект, и после получения ответа в куках окажутся данные с ключом "LastVisit":

Применение фильтров в ASP.NET Core MVC и C#

Если бы мы не использовали бы фильтр, то в каждом методе контроллера нам надо было бы определить строку кода:

HttpContext.Response.Cookies.Append("LastVisit", DateTime.Now.ToString("dd/MM/yyyy hh-mm-ss"));

Таким образом, фильтры позволяют нам избежать повторения в соответствии принципом DRY (Don't Repeat Yourself)

Выход из конвейера фильтров

Как показано выше, фильтры организованы в конвейер, в котором они последовательно выполняются. Однако мы можем на любом этапе выйти из этой конвейера, предотвратив выполнение последующих фильтров. Для этого надо установить свойство Result переданного в фильтр контекста.

Для этого добавим в проект новый фильтр:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace MvcApp.Filters
{
    public class FakeNotFoundResourceFilter : Attribute, IResourceFilter
    {
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            context.Result = new ContentResult { Content = "Ресурс не найден" };
        }
    }
}

Это фильтр ресурсов, который устанавливает свойство Result. Значение этого свойства фактически будет использоваться в качестве результата выполнения запроса.

Затем применим фильтр к методу контроллера:

using Microsoft.AspNetCore.Mvc;
using MvcApp.Filters;  // пространство имен фильтров

namespace MvcApp.Controllers
{
    public class HomeController : Controller
    {
        [FakeNotFoundResourceFilter]
        public IActionResult Index() => View();
    }
}

При обращении к методу Index сработает фильтр FakeNotFoundResourceFilter, который установит результат обработки запроса. И после него никакие другие фильтры не будут выполняться.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850