What are filters in ASP.NET Core MVC/Web API?
Quick Answer
Filters run code at specific stages of the MVC action pipeline, inside model binding and routing. The types, in order: Authorization → Resource → Action (before/after the action) → Exception → Result (before/after the result executes). Use them for cross-cutting concerns that need access to the action context — validation, logging with model data, caching, uniform exception-to-response mapping.
Filters let me run code at specific points in the MVC action pipeline — after routing and model binding, which is the part middleware can't see. There are authorization filters that run first, resource filters around model binding, action filters that run right before and after the action method, exception filters, and result filters around the response. I use them for cross-cutting things that need the action context: validating ModelState in one place, logging with the bound parameters, response caching, and mapping exceptions to a consistent error body. The distinction from middleware is that middleware is global and framework-agnostic, whereas a filter knows about the action, its arguments and ModelState.
Filter types:
- Authorization filters — run first;
[Authorize]. Short-circuit unauthenticated/unauthorised requests. - Resource filters — around everything after authorization, including model binding; good for caching or short-circuiting early.
- Action filters —
OnActionExecuting/OnActionExecuted; inspect or modify arguments and the result. Common forModelStatevalidation, logging. - Exception filters — handle unhandled exceptions from the action, turn them into a response.
- Result filters — around result execution; modify the response, add headers.
Filters vs middleware: middleware is framework-agnostic and runs for every request, without knowing about actions or model state. Filters are MVC-aware — they see the action, its arguments, ModelState, and the selected result. Choose middleware for truly global concerns (HTTPS, CORS, request logging); choose a filter when you need MVC context.
Register per-action ([ServiceFilter]/attribute), per-controller, or globally in AddControllers(o => o.Filters.Add(...)).
public class ValidateModelFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext ctx)
{
if (!ctx.ModelState.IsValid)
ctx.Result = new BadRequestObjectResult(ctx.ModelState);
}
public void OnActionExecuted(ActionExecutedContext ctx) { }
}
// Program.cs — apply globally
builder.Services.AddControllers(o => o.Filters.Add<ValidateModelFilter>());