Фильтры в ASP.NET Core MVC. Передача параметров и зависимостей в фильтры

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

Передача параметров в фильтр

Чтобы передать какие-либо параметры в фильтр, необходимо создать конструктор класса фильтра с параметрами. Например,

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

namespace AspFilterParameters.Filters
{
    public class TokenActionFilter : Attribute, IActionFilter
    {
        private readonly string _token;

        public TokenActionFilter(string token) 
        {
            _token = token;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {

        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            if ((_token == null) || (_token != "12345"))
            {
                context.Result = new UnauthorizedResult();
            }
        }

    }
}

Здесь в параметрах конструктора класса TokenActionFilter передается некий токен, значение которого, в дальнейшем, проверяется в методе OnActionExecuting() и, в зависимости от проверки, выдается результат UnauthorizedResult с кдом статуса 401. Применим этот фильтр, например, к методу контроллера HomeController:

using AspFilterParameters.Models;
using Microsoft.AspNetCore.Mvc;

using AspFilterParameters.Filters;

namespace AspFilterParameters.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            return View();
        }

        [TokenActionFilter(token:"45678")]
        public IActionResult Privacy()
        {
            return View();
        }
   }
}

Теперь запустим приложение и попробуем перейти на страницу Privacy:

Если изменить значение параметра фильтра так:

[TokenActionFilter(token:"12345")]
public IActionResult Privacy()
{
    return View();
}

то мы свободно перейдем на страницу:

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

builder.Services.AddControllersWithViews(options=>options.Filters.Add(new TokenActionFilter("12345")));

Передача зависимостей в фильтры

При передаче в фильтр зависимостей в ASP.NET Core MVC имеется небольшой нюанс. Например, создадим следующий фильтр:

using Microsoft.AspNetCore.Mvc.Filters;

namespace AspFilterParameters.Filters
{
    public class LogResourceFilter : Attribute, IResourceFilter
    {
        ILogger logger;
        public LogResourceFilter(ILoggerFactory factory) 
        {
            logger = factory.CreateLogger("LogResourceFilter");
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            logger.LogInformation("OnResourceExecuted");
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            logger.LogInformation("OnResourceExecuting");
        }
    }
}

Теперь попробуем применить этот фильтр к контроллеру (или методу):

[LogResourceFilter]
public class HomeController : Controller

Мы получим ошибку следующего содержания:

Ошибка CS7036 Отсутствует аргумент, соответствующий требуемому параметру «factory» из «LogResourceFilter.LogResourceFilter(ILoggerFactory)».

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

builder.Services.AddControllersWithViews(options=>options.Filters.Add<LogResourceFilter>());

Чтобы решить проблему с фильтрами для методов или контроллеров мы должны воспользоваться одним из атрибутов: ServiceFilterAttribute или TypeFilterAttribute.

ServiceFilterAttribute

Этот атрибут напрямую извлекает экземпляр фильтра из контейнера DI. Чтобы воспользоваться этим атрибутом, в параметрах атрибута необходимо передать тип фильтра, а сам фильтр зарегистрировать в качестве сервиса, например:

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);

    builder.Services.AddTransient<LogResourceFilter>();//регистрируем фильтр как сервис
    ///другой код метода Main
[ServiceFilter(typeof(LogResourceFilter))]
public class HomeController : Controller
{
  //код контроллера
}

Теперь фильтр будет работать:

TypeFilterAttribute

Класс TypeFilterAttribute создает объект фильтра с помощью фабрики Microsoft.Extensions.DependencyInjection.ObjectFactory, а с помощью механизма DI устанавливает все зависимости для создаваемого фильтра:

[TypeFilter(typeof(LogResourceFilter))]
public class HomeController : Controller
{
  //код контроллера
}

при этом, TypeFilterAttribute не требует, чтобы фильтр был зарегистрирован как сервис.

Итого

При необходимости, мы можем передавать в фильтры различные параметры и зависимости. Чтобы передать в фильтр параметры или зависимости, необходимо определить конструктор фильтра с необходимыми параметрами. Все параметры фильтра должны быть определены в конструкторе, поэтому, для передачи в фильтр сервисов мы должны использовать дополнительные атрибуты TypeFilterAttribute или ServiceFilterAttribute, чтобы передать в фильтр сервис.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии