What are filters in ASP.NET Core?

Updated Apr 28, 2026

Short answer

Filters in ASP.NET Core are components that allow developers to run code before or after specific stages of request processing, especially around controller actions and Razor Pages handlers. They are commonly used for cross-cutting concerns such as authorization, validation, logging, exception handling, and modifying action results. Unlike middleware, filters operate closer to the MVC/action execution level and have access to action-specific context.

Deep explanation

Filters are part of the ASP.NET Core MVC pipeline and provide a way to execute reusable logic around controller actions, Razor Pages handlers, or endpoint execution.

They are useful when the logic is related to MVC behavior rather than the entire HTTP request pipeline.

Examples of cross-cutting concerns handled by filters:

  • Authorization checks.
  • Request validation.
  • Logging action execution.
  • Exception handling.
  • Modifying responses.
  • Measuring action performance.
  • Adding common headers.

The general flow is:

TEXT
HTTP Request
|
v
Middleware Pipeline
|
v
Routing
|
v
Authorization Filters
|
v
Resource Filters
|
v
Action Filters
|
v
Controller Action
|
v
Result Filters
|
v
HTTP Response

Filters execute inside the MVC execution pipeline, after routing has selected an endpoint.

Why use filters?

Without filters, developers often repeat the same code in multiple controllers.

Example:

CSHARP
public IActionResult CreateOrder(Order order)
{
Console.WriteLine("Starting action");
// Business logic
Console.WriteLine("Action completed");
return Ok();
}

If every action needs logging, adding this code repeatedly creates:

  • Duplicate code.
  • Maintenance problems.
  • Higher chances of inconsistency.

A filter allows the behavior to be defined once and reused.

Types of filters in ASP.NET Core

ASP.NET Core provides several built-in filter types.

Authorization filters

Authorization filters run first in the filter pipeline.

Their purpose is to determine whether a request is allowed.

Example:

CSHARP
[Authorize]
public IActionResult GetOrders()
{
return Ok();
}

The [Authorize] attribute internally uses authorization filtering.

If authorization fails:

  • The action is not executed.
  • A 401 or 403 response is returned.

Resource filters

Resource filters run after authorization but before model binding.

They are useful for tasks that need to happen early.

Common uses:

  • Caching.
  • Short-circuiting requests.
  • Resource-level processing.

Example scenario:

A resource filter checks whether a response exists in cache before executing model binding and controller logic.

Action filters

Action filters run immediately before and after a controller action.

They are the most commonly used filters.

Example:

CSHARP
public class LoggingFilter : IActionFilter
{
public void OnActionExecuting(
ActionExecutingContext context)
{
Console.WriteLine(
"Action starting");
}
public void OnActionExecuted(
ActionExecutedContext context)
{
Console.WriteLine(
"Action completed");
}
}

Flow:

TEXT
Request
|
v
OnActionExecuting()
|
v
Controller Action
|
v
OnActionExecuted()
|
v
Response

Exception filters

Exception filters handle exceptions thrown during MVC action execution.

Example:

CSHARP
public class GlobalExceptionFilter
: IExceptionFilter
{
public void OnException(
ExceptionContext context)
{
context.Result =
new ObjectResult(
"Something went wrong")
{
StatusCode = 500
};
}
}

They are useful for:

  • Returning consistent error responses.
  • Logging controller exceptions.

However, middleware is usually preferred for global exception handling because it can catch errors outside the MVC pipeline as well.

Result filters

Result filters execute before and after an action result is executed.

Example:

CSHARP
public class HeaderFilter : IResultFilter
{
public void OnResultExecuting(
ResultExecutingContext context)
{
context.HttpContext.Response.Headers
.Add("X-App-Version", "1.0");
}
public void OnResultExecuted(
ResultExecutedContext context)
{
}
}

Common uses:

  • Adding response headers.
  • Modifying output formatting.
  • Logging response information.

Creating custom filters

Filters can be created by implementing filter interfaces.

Example action filter:

CSHARP
public class TimingFilter : IActionFilter
{
private Stopwatch _timer;
public void OnActionExecuting(
ActionExecutingContext context)
{
_timer = Stopwatch.StartNew();
}
public void OnActionExecuted(
ActionExecutedContext context)
{
_timer.Stop();
Console.WriteLine(
$"Execution time: {_timer.ElapsedMilliseconds}ms");
}
}

Registering filters

Filters can be registered in different ways.

Attribute registration

Apply a filter directly to a controller or action.

CSHARP
[ServiceFilter(typeof(TimingFilter))]
public IActionResult GetProducts()
{
return Ok();
}

Global registration

Register a filter for all controllers.

CSHARP
builder.Services
.AddControllers(options =>
{
options.Filters.Add<TimingFilter>();
});

Every controller action will now execute this filter.

Dependency injection with filters

Filters can use dependency injection.

Example:

CSHARP
public class AuditFilter : IActionFilter
{
private readonly ILogger<AuditFilter> _logger;
public AuditFilter(
ILogger<AuditFilter> logger)
{
_logger = logger;
}
public void OnActionExecuting(
ActionExecutingContext context)
{
_logger.LogInformation(
"Action executed");
}
public void OnActionExecuted(
ActionExecutedContext context)
{
}
}

This makes filters easier to test and maintain.

Synchronous vs asynchronous filters

ASP.NET Core provides synchronous and asynchronous filter interfaces.

Synchronous example:

CSHARP
IActionFilter

Asynchronous example:

CSHARP
IAsyncActionFilter

Async filters are preferred when performing asynchronous operations.

Example:

CSHARP
public class ValidationFilter
: IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
// Before action
var result = await next();
// After action
}
}

Filters vs Middleware

Filters and middleware are often confused because both can execute code before and after processing.

MiddlewareFilters
Runs for every HTTP requestRuns within MVC/action execution
Works before endpoint executionWorks around controller actions
Has access to HTTP pipelineHas access to MVC context
Good for authentication, logging, exceptionsGood for action-level concerns

Example:

Use middleware for:

  • Global exception handling.
  • Request logging.
  • Authentication.
  • Compression.

Use filters for:

  • Validating controller inputs.
  • Auditing actions.
  • Modifying action results.

Filters vs Dependency Injection services

Filters should not replace normal application services.

A good separation:

TEXT
Controller
|
v
Service Layer
|
v
Business Logic
Filter
|
v
Cross-cutting Concerns

Business rules belong in services, while filters handle reusable infrastructure behavior.

Real-world example

Consider an e-commerce API where every order creation request must be logged.

Without a filter:

CSHARP
public IActionResult CreateOrder(Order order)
{
logger.LogInformation(
"Creating order");
// Save order
return Ok();
}

This logging code would be repeated across many actions.

Create a filter:

CSHARP
public class AuditFilter : IActionFilter
{
private readonly ILogger<AuditFilter> _logger;
public AuditFilter(
ILogger<AuditFilter> logger)
{
_logger = logger;
}
public void OnActionExecuting(
ActionExecutingContext context)
{
_logger.LogInformation(
"Executing {Action}",
context.ActionDescriptor.DisplayName);
}
public void OnActionExecuted(
ActionExecutedContext context)
{
_logger.LogInformation(
"Action completed");
}
}

Register globally:

CSHARP
builder.Services.AddControllers(options =>
{
options.Filters.Add<AuditFilter>();
});

Now every controller action automatically receives audit logging without adding duplicate code.

Common mistakes

  • * Using filters for logic that belongs in application services.
  • * Confusing filters with middleware and choosing the wrong execution level.
  • * Adding too many global filters and making request flow difficult to understand.
  • * Performing long-running operations inside filters.
  • * Using synchronous filters when asynchronous work is required.
  • * Forgetting that authorization filters run before action filters.
  • * Handling all application exceptions only with exception filters instead of middleware.
  • * Registering filters incorrectly and causing dependency injection failures.
  • * Assuming filters run for every type of endpoint, including non-MVC endpoints.
  • * Modifying response data in filters without understanding serialization behavior.

Follow-up questions

  • What is the difference between middleware and filters in ASP.NET Core?
  • What are the different types of filters in ASP.NET Core?
  • When would you choose a filter instead of middleware?
  • How do you create a custom action filter?
  • Why are asynchronous filters preferred for some scenarios?
  • Can filters use dependency injection in ASP.NET Core?

More .NET Core interview questions

View all →