Home > OS >  How can I use dependency injection in asp.net core action filter?
How can I use dependency injection in asp.net core action filter?

Time:07-21

I have a FilterAttribute that has two parameters, one defined in dependency injection and one defined on method of controller as as string

public controller : ControllerBase 
{
    [MyFilter("Parameter1", FromDependency)]
    public ActionResult MyMethod()
    {
         ....
    }
}

and the filter

public MyFilter : Attribute
{
    MyFilter(string parameter1, context fromDependency)
    {
    }
}

How can I inject the parameter from dependency injection?

CodePudding user response:

You can implement an IFilterFactory for this purpose. The runtime checks for this interface when creating filters and calls the CreateInstance method that gets an IServiceProvider as a parameter. You can use this provider to create services and inject them into the filter.

The following sample is taken from the docs:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }
}

If you need to both use services from DI and values defined on the attribute, you can use the following approach:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    private readonly string _attrParam;

    public ResponseHeaderFilterFactory(string attrParam)
    {
      _attrParam = attrParam;
    }

    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) 
    {
        var svc = serviceProvider.GetRequiredService<IMyService>();
        return new InternalResponseHeaderFilter(_attrParam, svc);
    }

    private class InternalResponseHeaderFilter : IActionFilter
    {
        private readonly string _attrParam;
        private readonly IMyService _service;

        public InternalResponseHeaderFilter(string attrParam, IMyService service)
        {
          _attrParam = attrParam;
          _service = service;
        }

        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }
}

You can then apply the filter like this:

public controller : ControllerBase 
{
    [ResponseHeaderFilterFactory("Parameter1")]
    public ActionResult MyMethod()
    {
         ....
    }
}

CodePudding user response:

You can implement ActionFilterAttribute to get DI dependencies from HttpContext.RequestServices:

public sealed class MyAttr : ActionFilterAttribute
{
    MyAttr(string parameter1)
    {
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        //Get dependency from HttpContext services
        var myDependency = context.HttpContext.RequestServices.GetService<MyDependency>();

        //Use it
        myDependency.DoSomething();

        //....
    }
}

CodePudding user response:

Injecting components into action filter attributes directly is not possible but there are various workarounds to allow us to effectively accomplish the same thing. Using ServiceFilter is a relatively clean way to allow dependency injection into individual action filters.

The ServiceFilter attribute can be used at the action or controller level. Usage is very straightforward:

[ServiceFilter(typeof(MyFilter))]

And our filter:

public class MyFilter: IActionFilter
 { 
    MyFilter(string parameter1, context fromDependency)
    {
    }
 }

Obviously, as we are resolving our filter from the IoC container, we need to register it:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<MyFilter>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
            "parameter1"));
    ...
}

more details in Paul Hiles article: here

  • Related