Action Filters are one of the most powerful features in ASP.NET Core. They allow you to execute logic **before** and **after** an individual action method runs. This is perfect for "Cross-Cutting Concerns" like logging, performance tracking, and security checks that you don't want to repeat in every action.
"Action Filters help you keep your controllers clean by moving repetitive logic into reusable classes."
Action filters implement the IActionFilter or IAsyncActionFilter interfaces. They provide two main methods:
In a professional enterprise app, you need to know which actions are slow. This filter logs the execution time of every request it's applied to.
using Microsoft.AspNetCore.Mvc.Filters;
using System.Diagnostics;
public class TrackPerformanceFilter : IActionFilter
{
private Stopwatch _stopwatch;
public void OnActionExecuting(ActionExecutingContext context)
{
_stopwatch = Stopwatch.StartNew();
}
public void OnActionExecuted(ActionExecutedContext context)
{
_stopwatch.Stop();
var elapsed = _stopwatch.ElapsedMilliseconds;
var actionName = context.ActionDescriptor.DisplayName;
Console.WriteLine($"[PERF] Action {actionName} took {elapsed}ms");
}
}
In banking or healthcare apps, you must log exactly who accessed what data. This filter automatically captures user ID and action details.
public class AuditLogFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 1. Logic BEFORE the action runs
var user = context.HttpContext.User.Identity.Name ?? "Anonymous";
var ip = context.HttpContext.Connection.RemoteIpAddress;
Console.WriteLine($"[AUDIT] User {user} from {ip} is accessing {context.ActionDescriptor.DisplayName}");
// 2. Execute the action
var resultContext = await next();
// 3. Logic AFTER the action runs
if (resultContext.Exception != null) {
Console.WriteLine("[AUDIT] Action failed with exception!");
}
}
}
Sometimes you want to ensure all strings are "trimmed" or all dates are in "UTC" before saving them to the database. You can do this automatically in a filter.
public class ModelSanitizerFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
foreach (var arg in context.ActionArguments.Values)
{
if (arg is string stringValue)
{
// Automatically trim all string parameters!
// This prevents " John Doe " from being saved with spaces.
}
}
}
public void OnActionExecuted(ActionExecutedContext context) { /* No-op */ }
}
[ServiceFilter(typeof(MyFilter))] on an action/controller, or globally in Program.cs using builder.Services.AddControllersWithViews(options => options.Filters.Add()) .