Search code examples
c#.net-coredependency-injectionasp.net-core-webapinlog

Not able to use DI for logging in AuthorizationFilter


I still don’t have Dependency Injection down and I’m new with .Net Core (using 3.1.5). I’ve created an AuthorizationFilter for a WebAPI. What I’m still not understanding is how I can pass my Nlog to the attribute.

I have the following as my AuthorizaitonFilter

public class TokenAttribute : Attribute, IAuthorizationFilter
    {
        private readonly ILogger _logger;

        public TokenAttribute(ILogger logger)
        {
            _logger = logger;
        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            _logger.LogInformation("Authorizing");

            //Get the values from the Request Header
            context.HttpContext.Request.Headers.TryGetValue("Api-Token", out var token);
            context.HttpContext.Request.Headers.TryGetValue("Customer", out var customer);

            //Query to look up Customer and Token

            using (var ctx = new ModelContext())
            {
                var results = ctx.Token.FromSqlRaw(sbQuery.ToString()).ToList();

                if (results == null || results.Count == 0)
                {
                    context.Result = new BadRequestObjectResult("Invalid Token");
                    return;
                }

                return;                
            }
        } 
    }        
}

However, I’m getting an error on my Token attribute. If I leave it as just [Token] I complains with a message: “There is no argument given that corresponds to the required formal parameter”. I get that because the Token attribute’s constructor is looking for a ILogger.

[Token]
        [AcceptVerbs(WebRequestMethods.Http.Get, WebRequestMethods.Http.Post)]
        public ShiftRequest Post([FromBody] ShiftRequest m)
        {
            _logger.LogInformation("Calling POST command.");

Then if I try to pass _logger to the Token Attribute it complains with “An object reference is required for the non-static field ‘ShiftController._logger’”.

[ApiController]
[Route("[controller]")]
public class ShiftController : ControllerBase
{
    private IConfiguration _config;
    private readonly ILogger<ShiftController> _logger;

    public ShiftController(ILogger<ShiftController> logger, IConfiguration config)
    {
        _config = config;
        _logger = logger;
    }
        
    [Token(_logger)]
    [AcceptVerbs(WebRequestMethods.Http.Get, WebRequestMethods.Http.Post)]
    public ShiftRequest Post([FromBody] ShiftRequest m)
    {
        _logger.LogInformation("Calling POST command.");

UPDATED CODE

public class TokenAuthorizationFilter : IAuthorizationFilter
    {
        private readonly ILogger<TokenAuthorizationFilter> _logger;
        public TokenAuthorizationFilter(ILogger<TokenAuthorizationFilter> logger)
        {
            _logger = logger;
        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            _logger.LogInformation("Authorizing Token");

            //Get the values from the Request Header
            context.HttpContext.Request.Headers.TryGetValue("Api-Token", out var token);
            context.HttpContext.Request.Headers.TryGetValue("Customer", out var customer);


//Query to check username and password

            using (var ctx = new ModelContext())
            {
                var results = ctx.Token.FromSqlRaw(sbQuery.ToString()).ToList();

                if (results == null || results.Count == 0)
                {
                    context.Result = new BadRequestObjectResult("Invalid Token");
                    return;
                }

                if (!context.ModelState.IsValid)
                {
                    context.Result = new BadRequestObjectResult(context.ModelState);
                }

                return;
            }
        }

Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<TokenAuthorizationFilter>();
            services.AddControllersWithViews();
        }

Controller

 [ServiceFilter(typeof(TokenAuthorizationFilter))]
        [AcceptVerbs(WebRequestMethods.Http.Get, WebRequestMethods.Http.Post)]
        public ShiftRequest Post([FromBody] ShiftRequest m)
        {
            _logger.LogInformation("Calling POST command.");

Solution

  • You don't need to subclass Attribute in this case. For authorization filters, these can be added via a [ServiceFilter(type)] attribute on the action. When the ServiceFilter attribute is used, all dependencies are injected automatically via ASP.NET Core's built-in service provider.

    In your code, this would look something like [ServiceFilter(typeof(TokenAttribute))], although as a personal suggestion it might be more appropriate to name it something like TokenAuthorizationFilter.

    Note that you'll have to register your TokenAttribute as a service inside Startup.cs or wherever your HTTP pipeline setup is happening.