ASP.NET Core 8 Web API -

Filters

In ASP.NET Core, filters are a critical part of the request processing pipeline, allowing you to execute code before or after certain stages of the request and response. This advanced tutorial will cover built-in filters, creating custom filters, dependency injection, order of execution, and advanced configurations like suppressing the automatic model state validation.


1. Introduction to Filters

Filters in ASP.NET Core can be applied at the global, controller, or action level. They are used for cross-cutting concerns such as authorization, logging, and exception handling. The main types of filters are:


2. Built-in Filters

ASP.NET Core provides several built-in filters to perform common tasks.

Example: Using Built-in Filters

        
            
[Authorize]
[ResponseCache(Duration = 60)]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetProducts()
    {
        return Ok(new List<string> { "Product1", "Product2" });
    }
}

        
    

3. Custom Filters

Creating custom filters involves implementing the appropriate filter interface or deriving from an abstract base class.

Example: Custom Action Filter

Create the Custom Action Filter
        
            
public class LogActionFilter : IActionFilter
{
    private readonly ILogger<LogActionFilter> _logger;

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

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation("Executing action: {ActionName}", context.ActionDescriptor.DisplayName);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        _logger.LogInformation("Executed action: {ActionName}", context.ActionDescriptor.DisplayName);
    }
}

        
    
Register the Custom Action Filter

Globally:

        
            
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.Filters.Add<LogActionFilter>();
    });
}

        
    

At the Controller/Action Level:

        
            
[ServiceFilter(typeof(LogActionFilter))]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetProducts()
    {
        return Ok(new List<string> { "Product1", "Product2" });
    }
}

        
    

Example: Custom Exception Filter

Create the Custom Exception Filter
        
            
public class CustomExceptionFilter : IExceptionFilter
{
    private readonly ILogger<CustomExceptionFilter> _logger;

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

    public void OnException(ExceptionContext context)
    {
        _logger.LogError(context.Exception, "An unhandled exception occurred.");
        context.Result = new ObjectResult("An error occurred.")
        {
            StatusCode = StatusCodes.Status500InternalServerError
        };
        context.ExceptionHandled = true;
    }
}

        
    
Register the Custom Exception Filter

Globally:

        
            
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.Filters.Add<CustomExceptionFilter>();
    });
}

        
    

4. Dependency Injection in Filters

Filters can use dependency injection to obtain services.

Example: Filter with Dependency Injection

        
            
public class CustomActionFilter : IActionFilter
{
    private readonly ICustomService _customService;

    public CustomActionFilter(ICustomService customService)
    {
        _customService = customService;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _customService.PerformOperation();
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

public static class CustomActionFilterExtensions
{
    public static IServiceCollection AddCustomActionFilter(this IServiceCollection services)
    {
        services.AddScoped<CustomActionFilter>();
        return services;
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddCustomActionFilter();
    services.AddControllers();
}

[ServiceFilter(typeof(CustomActionFilter))]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetProducts()
    {
        return Ok(new List<string> { "Product1", "Product2" });
    }
}

        
    

5. Order of Execution

The order of execution for filters is crucial and follows a specific sequence:

Example: Order of Execution

        
            
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.Filters.Add(new AuthorizeFilter());
        options.Filters.Add(new LogActionFilter());
        options.Filters.Add(new CustomExceptionFilter());
    });
}

        
    

6. Suppressing Automatic Model State Validation

By default, ASP.NET Core automatically validates the model state before executing an action. You can suppress this behavior if you need custom validation logic.

Example: Suppressing Model State Validation

        
            
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});

        
    

Then, you can perform custom validation in your action method.

        
            
[HttpPost]
public IActionResult CreateProduct(ProductModel model)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    // Your code here

    return Ok();
}

        
    

7. Combining Filters

You can combine multiple filters to create a robust and flexible request processing pipeline.

Example: Combining Filters

        
            
[Authorize]
[ServiceFilter(typeof(LogActionFilter))]
public class ProductsController : ControllerBase
{
    [HttpGet]
    [ResponseCache(Duration = 60)]
    public IActionResult GetProducts()
    {
        return Ok(new List<string> { "Product1", "Product2" });
    }
}

        
    

8. Summary

Filters in ASP.NET Core 8 Web API are a powerful feature that allows you to inject logic at various points in the request processing pipeline. By understanding and utilizing built-in filters, creating custom filters, using dependency injection, managing the order of execution, and handling advanced configurations like suppressing automatic model state validation, you can build highly maintainable and flexible web APIs. This comprehensive guide should help you master filters in ASP.NET Core 8 and apply best practices to your projects.