How can I use Dependency Injection in a .Net Core ActionFilterAttribute?

前端 未结 2 1012
灰色年华
灰色年华 2020-12-03 04:59

AuthenticationRequiredAttribute Class

public class AuthenticationRequiredAttribute : ActionFilterAttribute
{
    ILoginTokenKeyApi _loginTo         


        
相关标签:
2条回答
  • 2020-12-03 05:42

    As per the documentation, you have a few options here:

    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 or TypeFilter attributes

    If you just want to get this working quickly, you can just use one of the first two options to apply your filter to a controller or a controller action. When doing this, your filter does not need to be an attribute itself:

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

    The ExampleActionFilter can then just implement e.g. IAsyncActionFilter and you can directly depend on things using constructor injection:

    public class ExampleActionFilter : IAsyncActionFilter
    {
        private readonly IMemoryCache _memoryCache;
        public ExampleActionFilter(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }
    
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        { … }
    }
    

    You can also use the [ServiceFilter] attribute instead to get the same effect but then you will also need to register your ExampleActionFilter with the dependency injection container in your Startup.

    Filter factory

    If you need more flexibility, you can implement your own filter factory. This allows you to write the factory code to create the actual filter instance yourself. A possible implementation for the above ExampleActionFilter could look like this:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class ExampleActionFilterAttribute : Attribute, IFilterFactory
    {
        public bool IsReusable => false;
    
        public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
        {
            return serviceProvider.GetService<ExampleActionFilter>();
        }
    }
    

    You can then use that [ExampleActionFilter] attribute to make the MVC framework create an instance of the ExampleActionFilter for you, using the DI container.

    Note that this implementation is basically the same thing that ServiceFilterAttribute does. It’s just that implementing it yourself avoids having to use the ServiceFilterAttribute directly and allows you to have your own attribute.

    Using service locator

    Finally, there is another quick option that allows you to avoid constructor injection completely. This uses the service locator pattern to resolve services dynamically when your filter actually runs. So instead of injecting the dependency and using it directly, you retrieve it explicitly from the context:

    public class ExampleActionFilter : ActionFilterAttribute
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var memoryCache = context.HttpContext.RequestServices.GetService<IMemoryCache>();
    
            // …
        }
    }
    
    0 讨论(0)
  • 2020-12-03 05:49

    Instead of resolving at construction, ActionExecutingContext.HttpContext.RequestServices should give you a reference to the request's service container at the time of the request.

    So:

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var svc = filterContext.HttpContext.RequestServices;
        var memCache = svc.GetService<IMemoryCache>();
        //..etc
    
    0 讨论(0)
提交回复
热议问题