C# tutorials > Frameworks and Libraries > ASP.NET Core > What are filters in ASP.NET Core?

What are filters in ASP.NET Core?

Filters in ASP.NET Core are custom classes that provide a way to add pre-processing or post-processing logic to action methods in your controllers. They allow you to execute code before or after certain stages of the request processing pipeline, such as before an action method executes or after an action method returns a result. This is useful for cross-cutting concerns like authentication, authorization, logging, and exception handling.

Introduction to Filters

ASP.NET Core filters are attributes that you can apply to controller actions or entire controllers. They execute at different points in the request processing pipeline, allowing you to intercept and modify requests and responses. Common uses include validating input, checking permissions, or modifying the output.

Types of Filters

ASP.NET Core provides several types of filters, each executing at a specific stage of the pipeline:

  • Authorization Filters: Used to authorize access to actions. They run before any other filter.
  • Resource Filters: Used to perform tasks around the execution of the rest of the pipeline. They run before and after model binding.
  • Action Filters: Used to execute logic before (OnActionExecuting) and after (OnActionExecuted) an action method is called. They can modify the action's inputs or the result.
  • Exception Filters: Used to handle exceptions thrown during the execution of the pipeline. They handle unhandled exceptions.
  • Result Filters: Used to execute logic before (OnResultExecuting) and after (OnResultExecuted) the execution of a result (e.g., a ViewResult). They can modify the result before it is sent to the client.

Creating a Custom Action Filter

This example shows how to create a custom action filter that logs information before and after an action method executes. It implements the IActionFilter interface and uses an ILogger instance to write the log messages.

using Microsoft.AspNetCore.Mvc.Filters;

public class LogActionFilter : IActionFilter
{
    private readonly ILogger<LogActionFilter> _logger;

    public LogActionFilter(ILogger<LogActionFilter> logger)
    {
        _logger = logger;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation($"Action {{context.ActionDescriptor.DisplayName}} executing");
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        _logger.LogInformation($"Action {{context.ActionDescriptor.DisplayName}} executed");
    }
}

Applying the Filter to a Controller

This shows how to apply the LogActionFilter to an entire controller using the attribute syntax. Alternatively, you could apply it to a specific action method.

using Microsoft.AspNetCore.Mvc;

[LogActionFilter]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Privacy()
    {
        return View();
    }
}

Applying the Filter to a Specific Action

This shows how to apply the LogActionFilter to a specific action method. The ServiceFilterAttribute is used to resolve the filter from the dependency injection container. Remember to register `LogActionFilter` in your `ConfigureServices` method in `Startup.cs`.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

public class HomeController : Controller
{
    [ServiceFilter(typeof(LogActionFilter))]
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Privacy()
    {
        return View();
    }
}

Registering Filters Globally

This shows how to register the LogActionFilter globally, so it applies to every action method in your application. This is useful for cross-cutting concerns that should be applied consistently throughout the application. Note: You'll need to adjust the logger creation to fit your application's logging configuration.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        options.Filters.Add(new LogActionFilter(LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger<LogActionFilter>()));
    });
}

Concepts Behind the Snippet

The key concepts behind filters are the ability to intercept and modify the request processing pipeline. This allows for separation of concerns and cleaner code by encapsulating cross-cutting logic in reusable components. Filters promote code reusability and maintainability.

Real-Life Use Case Section

A common real-life use case is implementing authorization checks. You could create an authorization filter that checks if the user has the necessary permissions to access a specific action. Another example is implementing caching. A filter could check if the requested data is already cached and return the cached response instead of executing the action method.

Best Practices

  • Keep filters focused: Each filter should have a specific responsibility.
  • Use dependency injection: Use dependency injection to inject dependencies into your filters.
  • Avoid complex logic: Keep the logic in your filters simple and concise.
  • Consider filter order: The order in which filters are executed can be important. Use the Order property to control the order of execution.

Interview Tip

When discussing filters in an interview, be prepared to explain the different types of filters, their execution order, and how to create custom filters. Provide examples of real-world use cases for filters, such as authentication, authorization, and logging. Demonstrate your understanding of the benefits of using filters for separation of concerns and code reusability.

When to Use Them

Use filters when you need to apply cross-cutting concerns to multiple action methods or controllers. They are especially useful for tasks that need to be performed before or after the execution of an action method, such as authentication, authorization, logging, or exception handling.

Memory Footprint

Filters generally have a minimal memory footprint. The overhead is primarily due to the creation and execution of the filter instances. Using service filters and dependency injection can further optimize memory usage by allowing filters to be reused across multiple requests.

Alternatives

Alternatives to filters include middleware and action method attributes. Middleware provides a more general-purpose way to intercept and modify requests and responses, while action method attributes can be used to apply specific logic to individual action methods. Interceptors in a DI container are also a possibility, but less common in ASP.NET Core directly for request pipeline logic.

Pros

  • Separation of concerns: Filters allow you to separate cross-cutting concerns from your core application logic.
  • Code reusability: Filters can be reused across multiple action methods and controllers.
  • Maintainability: Filters make your code more maintainable by encapsulating cross-cutting logic in reusable components.
  • Testability: Filters can be easily tested in isolation.

Cons

  • Complexity: Filters can add complexity to your application if not used carefully.
  • Performance overhead: Filters can introduce a slight performance overhead, especially if they perform complex logic.
  • Debugging: Debugging filter-related issues can be challenging.

FAQ

  • What is the difference between Action Filters and Result Filters?

    Action Filters execute before and after an action method executes. Result Filters execute before and after the result of an action method (e.g., a ViewResult) is executed.

  • How do I register a filter globally?

    You can register a filter globally by adding it to the Filters collection in the ConfigureServices method in Startup.cs.

  • How do I inject dependencies into a filter?

    You can inject dependencies into a filter by using constructor injection. Register the filter as a service in the dependency injection container, and the container will automatically resolve the dependencies when the filter is created.