1

I'm having some difficulties getting a custom action filter to work in ASP.NET Core 3.1 Web API. I've followed this SO, as well as the Microsoft docs, but it's not working. I've created a simple filter (note: I need Dependency Injection);

public class LogFilterAttribute : ActionFilterAttribute, IFilterMetadata
{
    private readonly ILogger<LogFilterAttribute> _logger;

    public LogFilterAttribute(ILogger<LogFilterAttribute> logger)
    {
        _logger = logger;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        _logger.LogWarning("test");
        base.OnActionExecuting(actionContext);
    }
}

Notes:

  • ActionFilterAttribute is from System.Web.Http.Filters namespace.

  • I implemented IFilterMetadata (which is just a marker interface) as this seems to be required by ServiceFilter and TypeFilter.

I'm registering this in ConfigureServices of Startup.cs as follows:

services.AddScoped<LogFilterAttribute>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

and then applying this in my Web API controller as follows:

[ApiVersion("1.0")]
[ApiController]
[Route("v{version:apiVersion}/resources/{id}")]
public class ResourceController : ControllerBase
{
   private readonly ILogger<ResourceController> _logger;

   public ResourceController(ILogger<ResourceController> logger)
   {
      _logger = logger;
   }

   [HttpGet]
   [ServiceFilter(typeof(LogFilterAttribute))]
   public async Task<IActionResult> Get([FromRoute(Name = "id")] string id)
   {
      _logger.LogInformation($"{typeof(ResourceController)}.{nameof(Get)}");
      return Ok();
   }
}

I've tried with both ServiceFilter and TypeFilter, but to no avail - it just skips the break-point in filter and goes straight to my route logic. What am I doing wrong?

Do Nhu Vy
  • 33,131
  • 37
  • 143
  • 202
Ryan.Bartsch
  • 1,550
  • 11
  • 29

4 Answers4

1

Try implementing IActionFilter in place of ActionFilterAttribute

Ceemah Four
  • 152
  • 2
  • 8
0

In StartUp.cs, Add the filter in MVC pipeline.

   public void ConfigureServices(IServiceCollection services)
   {
        services.AddMvc(options =>
                    {
                        options.Filters.Add(typeof(LogFilterAttribute));
                    });
   }
Ravi
  • 302
  • 2
  • 9
0

You need to use this on the controller/method as you're using a type filter, isn't the logger already scoped within the configuration? if so you need a type filter

[TypeFilter(typeof(LogFilterAttribute))]

For my use, I don't need to add IFilterMetadata

Rav
  • 655
  • 4
  • 13
0

In the end I solved the issue by implementing IAsyncActionFilter and inheriting from Attribute as follows:

public class LogFilterActionFilterAttribute : Attribute, IAsyncActionFilter
{
   public LogFilterActionFilterAttribute(...)
   {
      ...
   }

   public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
   {
      ...
   }
}

I also override TypeFilterAttribute as follows:

   public class LogFilterAttribute : TypeFilterAttribute
   {
      public LogFilterAttribute (...) : base(typeof(LogFilterActionFilterAttribute))
      {
         Arguments = new object[] { ... };
      }
   }

So that I can decorate on controllers/routes as follows:

[ApiVersion("1.0")]
[ApiController]
[Route("v{version:apiVersion}/resources/{id}")]
public class ResourceController : ControllerBase
{
   private readonly ILogger<ResourceController> _logger;

   public ResourceController(ILogger<ResourceController> logger)
   {
      _logger = logger;
   }

   [HttpGet]
   [LogFilter(...)]
   public async Task<IActionResult> Get([FromRoute(Name = "id")] string id)
   {
      _logger.LogInformation($"{typeof(ResourceController)}.{nameof(Get)}");
      return Ok();
   }
}
Ryan.Bartsch
  • 1,550
  • 11
  • 29