Both ActionFilter and Middleware are powerful mechanisms in ASP.NET Core used to handle cross-cutting concerns, but they operate at different levels of the request pipeline and serve distinct purposes.
1. Basic Concept
Aspect |
Middleware |
ActionFilter |
Definition |
Handles incoming requests and outgoing responses at the application level. |
Handles logic before and after controller actions at the controller/action level. |
Pipeline |
Executes before the request reaches routing and MVC. |
Executes after routing but before/after controller actions. |
Scope |
Applies to the entire application. |
Applies to specific controllers or actions. |
2. Execution Flow
Aspect |
Middleware |
ActionFilter |
When It Runs |
Runs before routing, or globally throughout the pipeline. |
Runs before and after controller actions. |
Control Flow |
Uses await next() to continue the pipeline. |
Uses OnActionExecuting and OnActionExecuted methods. |
Execution Context |
Has access to the HttpContext (global request/response). |
Has access to the ActionContext (controller/action details). |
3. Use Cases
Middleware |
ActionFilter |
Global logging of requests and responses. |
Logging at the controller/action level. |
Authentication/Authorization across the app. |
Model validation for specific actions. |
Global exception handling (Error handling). |
Input validation and action-specific security checks. |
Response compression, caching, CORS policies. |
Custom logic before/after an action executes. |
Routing, static file serving, and session management. |
Result filtering or modifying the action result. |
4. Implementation
Middleware Example
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Pre-processing logic
Console.WriteLine("Middleware: Before next");
await _next(context); // Continue to the next middleware
// Post-processing logic
Console.WriteLine("Middleware: After next");
}
}
// Register Middleware in Program.cs or Startup.cs
app.UseMiddleware<CustomMiddleware>();
ActionFilter Example
public class CustomActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// Logic before the action executes
Console.WriteLine("ActionFilter: Before action execution");
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Logic after the action executes
Console.WriteLine("ActionFilter: After action execution");
}
}
// Apply to a specific controller or action
[CustomActionFilter]
public IActionResult MyAction()
{
return Ok("Hello World");
}
5. Key Differences
Feature |
Middleware |
ActionFilter |
Level |
Application-wide. |
Controller or action-specific. |
Execution Point |
Runs early in the pipeline, even before routing. |
Runs after routing but before/after actions. |
Context Access |
Accesses HttpContext (global request/response data). |
Accesses ActionContext (controller/action data). |
Flexibility |
Better for cross-cutting concerns (logging, auth). |
Better for action-specific logic (validation). |
Error Handling |
Handles global errors (exception middleware). |
Handles action-specific errors. |
Usage Scope |
Ideal for app-wide concerns. |
Ideal for controller/action concerns. |
6. When to Use?
- Use Middleware when:
- You need to handle global concerns like logging, authentication, authorization, error handling, or CORS.
- You need to manipulate the request/response pipeline before it reaches the routing system.
- Use ActionFilter when:
- You need logic tied specifically to controller actions (e.g., model validation, custom authorization).
- You want to manipulate action inputs or results.
7. Summary
Aspect |
Middleware |
ActionFilter |
Scope |
Global, application-wide. |
Specific to controllers/actions. |
Execution Timing |
Before routing and controller execution. |
Before and after controller actions. |
Best for |
Cross-cutting concerns (auth, logging, errors). |
Action-specific logic (validation, logging). |
Context Access |
HttpContext (global request data). |
ActionContext (action-specific data). |
Pipeline Control |
Uses await next() to continue. |
Uses OnActionExecuting /OnActionExecuted . |
In summary, Middleware is suited for global application behavior, while ActionFilter is best for controller/action-specific logic.