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.
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:
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" });
}
}
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);
}
}
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;
}
}
Globally:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.Filters.Add<CustomExceptionFilter>();
});
}
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" });
}
}
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());
});
}
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();
}
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" });
}
}
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.