ASP.NET Core Filters: Deep Dive - IndianTechnoEra
Latest update Android YouTube

ASP.NET Core Filters: Deep Dive

Filters are one of the most powerful features in ASP.NET Core MVC. They allow you to run code before or after specific stages in the request processing pipeline — but only for MVC/API actions (unlike middleware which runs for every request). Filters are a top interview topic and essential for cross-cutting concerns like logging, validation, caching, and exception handling.


📌 What Are Filters?

Filters are attributes or classes that execute logic at specific points during the execution of an MVC action. They run inside the MVC pipeline after routing has selected the action and before the action executes.

Key difference from Middleware:
- Middleware runs for every request (static files, images, etc.)
- Filters run only for actions (controllers/endpoints)


🔁 Filter Pipeline Execution Order (Critical)

Filters execute in a specific order. Understanding this is crucial for interviews:

  1. Authorization Filters – Run first. Determine if user is authorized.
  2. Resource Filters – Run after authorization. Can handle caching, model binding, etc.
  3. Action Filters – Run before and after action method execution.
  4. Exception Filters – Run when an unhandled exception occurs.
  5. Result Filters – Run before and after action result execution.

⚠️ Interview Tip: Remember the acronym A-R-A-E-R (Authorization, Resource, Action, Exception, Result).


📋 The 5 Filter Types – Detailed

1. 🔐 Authorization Filters

Interface: IAuthorizationFilter or IAsyncAuthorizationFilter
When: First to run, before model binding and action execution.
Use cases: Custom authorization logic, role checks, token validation.

public class CustomAuthFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        if (!context.HttpContext.User.Identity.IsAuthenticated)
        {
            context.Result = new UnauthorizedResult();
        }
    }
}

2. 💾 Resource Filters

Interface: IResourceFilter or IAsyncResourceFilter
When: After authorization, before model binding. Runs around the entire action pipeline.
Use cases: Caching, logging, disabling model binding temporarily.

public class CacheResourceFilter : IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        // Before everything – check cache
    }
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        // After action and result – store cache
    }
}

3. ⚡ Action Filters

Interface: IActionFilter or IAsyncActionFilter
When: Immediately before and after the action method executes.
Use cases: Input validation, logging, measuring execution time, modifying parameters.

public class LoggingActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine($"Action {context.ActionDescriptor.DisplayName} started");
    }
    public void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine($"Action finished. Exception: {context.Exception?.Message}");
    }
}

4. 🚨 Exception Filters

Interface: IExceptionFilter or IAsyncExceptionFilter
When: Only when an unhandled exception occurs in the action or subsequent filters.
Use cases: Global error handling, logging exceptions, returning custom error responses.

public class CustomExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        // Log the exception
        // context.ExceptionHandled = true; // Mark as handled
        context.Result = new ObjectResult("Something went wrong") 
        { 
            StatusCode = 500 
        };
    }
}

5. 🎨 Result Filters

Interface: IResultFilter or IAsyncResultFilter
When: Before and after the action result executes (e.g., ViewResult, JsonResult).
Use cases: Modifying response headers, compressing output, logging results.

public class HeaderResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add("X-Custom-Header", "MyValue");
    }
    public void OnResultExecuted(ResultExecutedContext context) { }
}

🏗️ Creating and Applying Filters

Way 1: As an Attribute (Most Common)

public class MyActionFilterAttribute : Attribute, IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context) { }
    public void OnActionExecuted(ActionExecutedContext context) { }
}

// Apply to controller or action
[MyActionFilter]
public class HomeController : Controller { }

Way 2: As a Service (With Dependency Injection)

public class LoggingFilter : IActionFilter
{
    private readonly ILogger _logger;
    public LoggingFilter(ILogger<LoggingFilter> logger) => _logger = logger;
    // ... implementation
}

// Register in Program.cs
builder.Services.AddScoped<LoggingFilter>();
builder.Services.AddControllers(options =>
{
    options.Filters.Add<LoggingFilter>(); // Global registration
});

Way 3: Using TypeFilter or ServiceFilter

[TypeFilter(typeof(LoggingFilter))] // Resolves from DI
public IActionResult Index() { }

🌐 Registering Filters at Different Levels

  • Global Level – Applies to all controllers/actions in the app.
    builder.Services.AddControllers(options => options.Filters.Add(new MyGlobalFilter()));
  • Controller Level – Applies to all actions in a controller.
    [MyFilter] public class HomeController : Controller { }
  • Action Level – Applies only to a specific action.
    [MyFilter] public IActionResult Index() { }

📌 Execution Order: Global → Controller → Action (outermost to innermost)


🎯 Real-World Examples

1. 🕒 Performance Profiling Filter

public class PerformanceFilter : IActionFilter
{
    private Stopwatch _stopwatch;
    public void OnActionExecuting(ActionExecutingContext context) => 
        _stopwatch = Stopwatch.StartNew();
    public void OnActionExecuted(ActionExecutedContext context)
    {
        _stopwatch.Stop();
        Console.WriteLine($"Action took {_stopwatch.ElapsedMilliseconds} ms");
    }
}

2. ✅ Model Validation Filter (Automatic)

public class ValidateModelFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
    public void OnActionExecuted(ActionExecutedContext context) { }
}

3. 🔁 Caching with Resource Filter

public class CacheResourceFilter : IResourceFilter
{
    private static readonly Dictionary<string, object> _cache = new();
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        string key = context.HttpContext.Request.Path;
        if (_cache.ContainsKey(key))
        {
            context.Result = new ObjectResult(_cache[key]);
        }
    }
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        if (context.Result is ObjectResult result && result.Value != null)
        {
            _cache[context.HttpContext.Request.Path] = result.Value;
        }
    }
}

🧠 Interview Questions & Answers (Must Know)

Q1: What is the difference between Middleware and Filters?

A: Middleware runs for every request (static files, images, etc.) and is part of the core pipeline. Filters run only for MVC/API actions and have access to MVC-specific features like model state, action arguments, and results. Filters also have a more granular lifecycle (OnActionExecuting, OnResultExecuted, etc.).

Q2: How do you short-circuit a request from a filter?

A: Set context.Result to an ActionResult (e.g., BadRequestResult). The pipeline will skip the action and any remaining filters of that type.

Q3: What is the difference between OnActionExecuted and OnResultExecuting?

A: OnActionExecuted runs after the action method but before the result is executed. OnResultExecuting runs after the result has been created but before it's executed (before writing to response).

Q4: Can a filter access the action's arguments or change them?

A: Yes, in OnActionExecuting, you can access and modify context.ActionArguments. This is useful for validation or transformation.

Q5: How do you handle exceptions in filters vs middleware?

A: Exception filters catch exceptions only from MVC actions. For global exception handling including static files, use UseExceptionHandler middleware. Combine both: middleware for general errors, exception filters for API-specific error responses.


✅ Best Practices

  • Keep filters single-responsibility – one filter does one thing.
  • Use async filters (IAsyncActionFilter) for I/O operations like database calls.
  • Prefer service filters over instantiating filters manually when DI is needed.
  • Don't overuse filters – middleware might be better for cross-cutting concerns that apply to all requests.
  • Always call base.OnActionExecuting(context) if inheriting from built-in filter attributes.
  • For APIs, use exception filters to return standardized error responses (e.g., Problem Details).

📊 Quick Reference Table

Filter TypeInterfaceWhen It RunsCommon Use
AuthorizationIAuthorizationFilterFirst, before everythingCustom auth logic
ResourceIResourceFilterAfter auth, before model bindingCaching
ActionIActionFilterBefore/after action methodLogging, validation
ExceptionIExceptionFilterWhen unhandled exception occursError handling
ResultIResultFilterBefore/after result executionModifying response

🎯 Final Key Takeaway

Filters give you fine-grained control over MVC action execution. They are perfect for cross-cutting concerns specific to your API or web app. Understanding the filter pipeline order (Authorization → Resource → Action → Exception → Result) and when to use each type is critical for both real-world applications and .NET interviews.

✅ Combine filters with middleware for a robust, maintainable ASP.NET Core application.

إرسال تعليق

Feel free to ask your query...
Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.