Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom filter attributes inject dependency

I'musing ASP.NET Web API and I need to have authorization so I've created custom authorization attribute

public class CustomAuthorizationAttribute : AuthorizeAttribute

In order to inject dependency inside constructor I have following :

        public CustomAuthorizationAttribute(IAccountBL accountBl)
    {
        _accountBL = accountBl;
    }

In IAccountBL I have method which interacts with database checking if user is authorized to make request. Inside Member API controller I've register that attribute

    [CustomAuthorization]
public class MemberController : ApiController

But I get following error

Project.Account.AccountBL' does not contain a constructor that takes 0 arguments

And if I register it like

[CustomAuthorization(IAccountBL)]

enter image description here

Thank you

like image 659
jasenkoh Avatar asked Aug 23 '13 15:08

jasenkoh


People also ask

How do you inject attributes?

That said, if you're intent on injecting into an attribute, you can use the ASP.NET Core MVC IApplicationModelProvider . The framework passes dependencies into the provider's constructor, and the provider can pass dependencies to the attribute's properties or methods. In your Startup, register your provider.

What is TypeFilterAttribute?

TypeFilterAttribute. TypeFilterAttribute is similar to ServiceFilterAttribute, but its type isn't resolved directly from the DI container. It instantiates the type by using Microsoft. Extensions.

How do I create a custom filter in .NET core?

You can create custom filter attributes by implementing an appropriate filter interface for which you want to create a custom filter and derive the FilterAttribute class to use that class as an attribute. For example, implement IExceptionFilter and the FilterAttribute class to create a custom exception filter.


2 Answers

Action filters are just attributes. You do not have control over when those attributes are instantiated by the CLR. One possibility is to write a marker attribute:

public class CustomAuthorizationAttribute : Attribute { }

and then the actual action filter:

public class CustomAuthorizationFilter : ActionFilterAttribute
{
    private readonly IAccountBL accountBL;
    public CustomAuthorizationFilter(IAccountBL accountBL)
    {
        this.accountBL = accountBL;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<CustomAuthorizationAttribute>().Any() || 
            actionContext.ActionDescriptor.GetCustomAttributes<CustomAuthorizationAttribute>().Any())
        {
            // here you know that the controller or action is decorated 
            // with the marker attribute so that you could put your code
        }
    }
}

and finally register it as a global action filter:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        ...

        IAccountBL accountBL = ...
        config.Filters.Add(new CustomAuthorizationFilter(accountBL));
    }
}

and finally you could use the marker attribute:

[CustomAuthorization]
public class MemberController : ApiController
{
    ...
}
like image 136
Darin Dimitrov Avatar answered Sep 23 '22 13:09

Darin Dimitrov


You can get dependency in your filter by using extension method GetDependencyScope for class HttpRequestMessage. It's not a canonical way for dependency injection, but can be used as workaround. A basic example may look like this:

    public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        var dependencyScope = context.Request.GetDependencyScope();
        var dependency = dependencyScope.GetService(typeof (MyDependencyType));
        //use your dependency here
    }

This method may be used with constructor injection to simplify unit testing:

public class MyAuthenticationFilter : Attribute, IAuthenticationFilter
{
    private Func<HttpRequestMessage, MyDependencyType> _dependencyFactory;

    public MyAuthenticationFilter() :
        this(request => (MyDependencyType)request.GetDependencyScope().GetService(typeof(MyDependencyType)))
    {
    }

    public MyAuthenticationFilter(Func<HttpRequestMessage, MyDependencyType> dependencyFactory)
    {
        _dependencyFactory = dependencyFactory;
    }

    public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        var dependencyScope = context.Request.GetDependencyScope();
        var dependency = dependencyFactory.Invoke(context.Request);
        //use your dependency here
    }

    public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    public bool AllowMultiple { get; private set; }
}
like image 27
Sergey Popov Avatar answered Sep 21 '22 13:09

Sergey Popov