web-dev-qa-db-fra.com

Injecter le service dans le filtre d'action

J'essaie d'injecter un service dans mon filtre d'action mais je ne reçois pas le service requis injecté dans le constructeur. Voici ce que j'ai

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");
        }
    }
}

Et je décore mon contrôleur comme suit:

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

Startup.cs

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

En utilisant ces articles comme référence:

Filtres d'action de base ASP.NET

Filtres d'action, filtres de service et filtres de type dans ASP.NET 5 et MVC 6

Utilisation du filtre en tant que ServiceFilter

Étant donné que le filtre sera utilisé en tant que ServiceType, il doit être enregistré auprès de l'infrastructure IoC. Si les filtres d'action étaient utilisés directement, cela ne serait pas nécessaire.

Startup.cs

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

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

    ...
}

Des filtres personnalisés sont ajoutés à la méthode du contrôleur MVC et à la classe de contrôleur à l'aide de l'attribut ServiceFilter, comme suit: 

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

Il y avait d'autres exemples de 

  • Utilisation du filtre en tant que filtre global

  • Utilisation du filtre avec les contrôleurs de base

  • Utiliser le filtre avec une commande

Jetez un coup d'oeil, essayez-les et voyez si cela résout votre problème.

J'espère que cela t'aides.

56
Nkosi

Filtres globaux

Vous devez implémenter 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);
    }
}

Dans la classe Startup au lieu d'enregistrer un filtre réel, vous enregistrez votre usine de filtres:

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

Une autre façon de résoudre ce problème. Vous pouvez obtenir votre service via Context comme dans le code suivant:

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");
    }
}

Veuillez noter que vous devez enregistrer ce service dans Startup.cs.

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

Après avoir lu cet article ASP.NET Core - Filtres MVC ASP.NET Core du monde réel (août 2016) Je l'ai implémenté comme suit:

Dans Starup.cs/ConfigureServices:

services.AddScoped<MyService>();

Dans 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();
        }
    }
}

Dans MyFooController.cs:

[MyFilter]
public IActionResult MyAction()
{
}

Edition: Vous pouvez passer des arguments tels que [MyFilter("Something")] à l'aide de la propriété Arguments de la classe TypeFilterAttribute: Comment ajouter un paramètre à un filtre d'action dans asp.net? (le code de rboe montre aussi comment injecter des choses (de la même manière))

8
A.J.Bauer

Alors que la question fait implicitement référence aux "filtres via attributs", il est néanmoins intéressant de souligner que l'ajout de filtres "globalement par type" prend en charge les ID immédiatement:

[Pour les filtres globaux ajoutés par type] toutes les dépendances du constructeur seront remplies par l'injection de dépendance (DI). Ajouter un filtre par type équivaut à filters.Add (new TypeFilterAttribute (typeof (MyFilter))) .. ... https://docs.Microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection

En ce qui concerne les filtres basés sur les attributs:

Les filtres implémentés en tant qu'attributs et ajoutés directement aux classes de contrôleur ou aux méthodes d'action ne peuvent pas avoir de dépendances de constructeur fournies par l'injection de dépendance (DI). En effet, les paramètres de constructeur des attributs doivent être fournis là où ils sont appliqués. Ceci est une limitation du fonctionnement des attributs . https://docs.Microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection

Cependant, comme mentionné dans les réponses précédentes au PO, il existe des moyens d'indirection qui peuvent être utilisés pour atteindre DI. Par souci d’exhaustivité, voici les liens vers les documents officiels:

0
B12Toaster