Как я могу использовать внедрение зависимостей в .Net Core ActionFilterAttribute?

AuthenticationRequiredAttribute - класс

public class AuthenticationRequiredAttribute : ActionFilterAttribute
{
    ILoginTokenKeyApi _loginTokenKeyApi;
    IMemoryCache _memoryCache;

    public AuthenticationRequiredAttribute(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;

        _loginTokenKeyApi = new LoginTokenKeyController(new UnitOfWork());
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var memory = _memoryCache.Get(Constants.KEYNAME_FOR_AUTHENTICATED_PAGES);

        string requestedPath = filterContext.HttpContext.Request.Path;

        string tokenKey = filterContext.HttpContext.Session.GetString("TokenKey")?.ToString();

        bool? isLoggedIn = _loginTokenKeyApi.IsLoggedInByTokenKey(tokenKey).Data;

        if (isLoggedIn == null ||
            !((bool)isLoggedIn) ||
            !Constants.AUTHENTICATED_PAGES_FOR_NORMAL_USERS.Contains(requestedPath))
        {
            filterContext.Result = new JsonResult(new { HttpStatusCode.Unauthorized });
        }
    }
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
    }
}

HomeController

public class HomeController : Controller
{
    IUserApi _userApi;
    ILoginTokenKeyApi _loginTokenKey;
    IMemoryCache _memoryCache;

    public HomeController(IUserApi userApi, ILoginTokenKeyApi loginTokenKey, IMemoryCache memoryCache)
    {
        _loginTokenKey = loginTokenKey;
        _userApi = userApi;

        _memoryCache = memoryCache;
    }

    [AuthenticationRequired] // There is AN ERROR !!
    public IActionResult Example()
    {
        return View();
    }
}

ОШИБКА :

Error CS7036 There is no argument given that corresponds to the required formal parameter 'memoryCache' of 'AuthenticationRequiredAttribute.AuthenticationRequiredAttribute(IMemoryCache)' Project.Ground.WebUI

Моя проблема на самом деле: я не могу использовать инъекцию зависимостей в классах атрибутов.

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

@Liam Этот вопрос касается ASP.NET MVC, а не ASP.NET Core, поэтому ответ здесь не применим.

poke 09.10.2018 18:22

Есть ли причина, по которой вы сегодня изменили принятый ответ? В моем ответе ниже выделены три распространенных подхода к этому, в то время как ответ спонсора фокусируется только на одном из них (который также часто считается антипаттерном).

poke 02.12.2020 16:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
42
3
20 360
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вместо разрешения при построении ActionExecutingContext.HttpContext.RequestServices должен предоставить вам ссылку на сервисный контейнер запроса во время запроса.

Так:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var svc = filterContext.HttpContext.RequestServices;
    var memCache = svc.GetService<IMemoryCache>();
    //..etc

using Microsoft.Extensions.DependencyInjection;

Brian T 12.11.2019 20:23

используя Memcache в фильтре действий и пытаясь получить значения, он всегда возвращает null

AYKHO 05.07.2020 16:18

Согласно документация, у вас есть несколько вариантов:

If your filters have dependencies that you need to access from DI, there are several supported approaches. You can apply your filter to a class or action method using one of the following:

  • ServiceFilterAttribute
  • TypeFilterAttribute
  • IFilterFactory implemented on your attribute

Атрибуты ServiceFilter или TypeFilter

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

[TypeFilter(typeof(ExampleActionFilter))]
public IActionResult Example()
    => View();

ExampleActionFilter может тогда просто реализовать, например, IAsyncActionFilter, и вы можете напрямую зависеть от вещей, используя внедрение конструктора:

public class ExampleActionFilter : IAsyncActionFilter
{
    private readonly IMemoryCache _memoryCache;
    public ExampleActionFilter(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    { … }
}

Вы также можете использовать атрибут [ServiceFilter] вместо этого, чтобы получить тот же эффект, но тогда вам также нужно будет зарегистрировать свой ExampleActionFilter с контейнером внедрения зависимостей в вашем Startup.

Завод фильтров

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

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExampleActionFilterAttribute : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return serviceProvider.GetService<ExampleActionFilter>();
    }
}

Затем вы можете использовать этот атрибут [ExampleActionFilter], чтобы инфраструктура MVC создала для вас экземпляр ExampleActionFilter, используя контейнер DI.

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

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

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

public class ExampleActionFilter : ActionFilterAttribute
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var memoryCache = context.HttpContext.RequestServices.GetService<IMemoryCache>();

        // …
    }
}

Общий метод GetService <T> является методом расширения. Это означает, что вам необходимо: using Microsoft.Extensions.DependencyInjection;

sal 18.11.2019 07:43

Другие вопросы по теме