Filters run at specific points in the pipeline around action execution — a lighter-weight alternative to full middleware when the logic genuinely only concerns MVC/API actions (it has access to action arguments and results, which raw middleware does not):
public class LogActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine($"Executing {context.ActionDescriptor.DisplayName}");
}
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine($"Executed {context.ActionDescriptor.DisplayName}");
}
}
[ServiceFilter(typeof(LogActionFilter))]
[HttpGet]
public IActionResult Get() => Ok();
| Filter type | Runs around |
|---|---|
| Authorization filters | Before anything else — deciding if the request is allowed to proceed at all |
| Resource filters | Before/after model binding — can short-circuit before binding even happens |
| Action filters | Immediately before/after the action method itself runs |
| Exception filters | Only when an unhandled exception occurs inside an action |
| Result filters | Immediately before/after the action's result is executed (written to the response) |
Global exception filters and middleware-based exception handling (previous section) overlap in purpose; the practical convention is middleware for truly app-wide, unhandled-anywhere error formatting, and exception filters for MVC-specific exception handling that needs access to action/controller context.