What are action filters and how do you create custom filters?

4 minintermediateASP.NET-Corefilterscross-cutting

Quick Answer

Action filters run custom logic at stages of the MVC action pipeline, ideal for cross-cutting concerns (logging, validation, caching, exception handling). The filter types run in order: Authorization, Resource, Action, Exception, and Result filters. You create one by implementing an interface like `IActionFilter`/`IAsyncActionFilter` (or deriving from `ActionFilterAttribute`) and registering it globally, on a controller, or on an action.

Detailed Answer

Action filters are attributes that add extra processing logic before or after specific stages in the request processing pipeline. They allow cross-cutting concerns like logging, caching, authorization, and exception handling.

Filter types and execution order:

  1. Authorization filters - Run first, verify authorization
  2. Resource filters - Run after authorization, good for caching
  3. Action filters - Run before and after action method execution
  4. Exception filters - Handle exceptions
  5. Result filters - Run before and after result execution

Creating a custom action filter:

// Method 1: Inherit from ActionFilterAttribute
public class LogActivityFilter : ActionFilterAttribute
{
    private readonly ILogger _logger;

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

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation($"Executing action: {context.ActionDescriptor.DisplayName}");
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        _logger.LogInformation($"Executed action: {context.ActionDescriptor.DisplayName}");
        base.OnActionExecuted(context);
    }
}

// Method 2: Implement IActionFilter interface
public class ValidateModelFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Logic after action execution
    }
}

// Async version
public class AsyncLoggingFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, 
        ActionExecutionDelegate next)
    {
        // Before action execution
        await next(); // Execute the action
        // After action execution
    }
}

Using filters:

// Apply to specific action
[LogActivityFilter]
public IActionResult GetUser(int id)
{
    return Ok();
}

// Apply to controller
[ValidateModelFilter]
public class UsersController : ControllerBase
{
}

// Register globally in Program.cs
builder.Services.AddControllers(options =>
{
    options.Filters.Add();
});

Related Resources