it-roy-ru.com

Внедрить сервис в фильтр действий

Я пытаюсь внедрить сервис в мой фильтр действий, но я не получаю требуемый сервис в конструкторе. Вот что у меня есть:

public class EnsureUserLoggedIn : ActionFilterAttribute
{
    private readonly ISessionService _sessionService;

    public EnsureUserLoggedIn()
    {
        // I was unable able to remove the default ctor 
        // because of compilation error while using the 
        // attribute in my controller
    }

    public EnsureUserLoggedIn(ISessionService sessionService)
    {
        _sessionService = sessionService;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Problem: _sessionService is null here
        if (_sessionService.LoggedInUser == null)
        {
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            context.Result = new JsonResult("Unauthorized");
        }
    }
}

И я украшаю свой контроллер так:

[Route("api/issues"), EnsureUserLoggedIn]
public class IssueController : Controller
{
}

Startup.cs

services.AddScoped<ISessionService, SessionService>();
46
hyde

Используя эти статьи в качестве ссылки:

Основные фильтры действий ASP.NET

Фильтры действий, служебные фильтры и фильтры типов в ASP.NET 5 и MVC 6

Использование фильтра в качестве ServiceFilter

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

Startup.cs

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc();

    services.AddScoped<ISessionService, SessionService>();
    services.AddScoped<EnsureUserLoggedIn>();

    ...
}

Пользовательские фильтры добавляются в метод контроллера MVC и класс контроллера с использованием атрибута ServiceFilter, например: 

[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
    // GET: api/issues
    [HttpGet]
    [ServiceFilter(typeof(EnsureUserLoggedIn))]
    public IEnumerable<string> Get(){...}
}

Были и другие примеры 

  • Использование фильтра в качестве глобального фильтра

  • Использование фильтра с базовыми контроллерами

  • Использование фильтра с заказом

Посмотрите, попробуйте и посмотрите, решит ли это вашу проблему.

Надеюсь это поможет.

56
Nkosi

Глобальные фильтры

Вам нужно реализовать IFilterFactory:

public class AuthorizationFilterFactory : IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        // manually find and inject necessary dependencies.
        var context = (IMyContext)serviceProvider.GetService(typeof(IMyContext));
        return new AuthorizationFilter(context);
    }
}

В классе Startup вместо регистрации фактического фильтра вы регистрируете свою фабрику фильтров:

services.AddMvc(options =>
{
    options.Filters.Add(new AuthorizationFilterFactory());
});
26
Andrei

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

public override void OnActionExecuting(ActionExecutingContext context)
{
    _sessionService = context.HttpContext.RequestServices.GetService<ISessionService>();
    if (_sessionService.LoggedInUser == null)
    {
        context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        context.Result = new JsonResult("Unauthorized");
    }
}

Обратите внимание, что вы должны зарегистрировать эту услугу в Startup.cs

services.AddTransient<ISessionService, SessionService>();
22
Igor Valikovsky

После прочтения этой статьи ASP.NET Core - Реальные фильтры ASP.NET Core MVC (август 2016 г.) я реализовал это следующим образом:

В Starup.cs/ConfigureServices:

services.AddScoped<MyService>();

В MyFilterAttribute.cs:

public class MyFilterAttribute : TypeFilterAttribute
{        
    public MyFilterAttribute() : base(typeof (MyFilterAttributeImpl))
    {

    }

    private class MyFilterAttributeImpl : IActionFilter
    {
        private readonly MyService _sv;

        public MyFilterAttributeImpl(MyService sv)
        {
            _sv = sv;
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {                
            _sv.MyServiceMethod1();
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _sv.MyServiceMethod2();
        }
    }
}

В MyFooController.cs:

[MyFilter]
public IActionResult MyAction()
{
}

Правка: Передача аргументов, таких как [MyFilter("Something")], может быть выполнена с использованием свойства Arguments класса TypeFilterAttribute: Как добавить параметр в фильтр действий в asp.net? (код rboe также показывает, как вводить вещи (таким же образом))

8
A.J.Bauer

Хотя вопрос неявно относится к «фильтрам по атрибутам», все же стоит подчеркнуть, что добавление фильтров «глобально по типу» поддерживает DI «из коробки»:

[Для глобальных фильтров, добавляемых по типу] любые зависимости конструктора будут заполняться путем внедрения зависимостей (DI). Добавление фильтра по типу эквивалентно filters.Add (new TypeFilterAttribute (typeof (MyFilter)))) . https://docs.Microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection

Что касается атрибутных фильтров:

Фильтры, которые реализованы как атрибуты и добавлены непосредственно в классы контроллера или методы действия, не могут иметь конструкторских зависимостей, обеспечиваемых внедрением зависимостей (DI). Это потому, что атрибуты должны иметь свои параметры конструктора, где они применяются. Это ограничение работы атрибутов . https://docs.Microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection

Однако, как уже упоминалось в предыдущих ответах на ФП, существуют способы косвенного обращения, которые можно использовать для достижения DI. Для полноты, вот ссылки на официальные документы:

0
B12Toaster