Filters in ASP.NET allow you to intercept an incoming HTTP request, do some sort of processing work and hand the (modified) request back to the pipeline to do other work, until the final output is generated and sent back to the caller. There are some built-in filters that are provided as part of the framework that you may have used, not knowing that they are filters… Authorize
, HandleError
and ResponseCache
are examples of these. You can also build your own. Anytime you have a cross-cutting need in your application – a requirement that applies to the application as a whole – such as logging, global error handling, etc., it’s worth checking to see if creating an ASP.NET filter would be an appropriate solution.
You may have also come across another term: middleware. Both filters and middleware work in a similar fashion in that both are taking the incoming HTTP request, operating on that request in some way and then letting other processes in the pipeline takeover until that request is handled as a whole. However, filters are an ASP.NET MVC construct that comes into effect within that context. Middleware on the other hand, operates at a higher level and can handle requests even before it hits a particular route (or action method).
Types of Filters in ASP.NET
- Authorization Filter: Allows you to write custom logic to determine whether a particular caller should be given access to a particular endpoint.
- Resource Filter: Runs after Authorization filter and then encloses the rest of the pipeline. Allows you to short-circuit or bypass the rest of the pipeline.
- Action Filter: Runs right before and/or right after an action method executes. It can be used to alter the input parameters or the output of the action method.
- Result Filter: Executes after an action method completes executing but before an ActionResult executes.
- Exception Filter: Executes when an exception occurs in an action method.
Create Your Own ASP.NET Filter
Let’s create a simple action filter. You can use the same concept to create any of the other types of filters by simply replacing the events targeted for other ones. To follow along, create a brand new ASP.NET web application. If you are familiar with the dotnet CLI, you can create a new one by using the command dotnet new webapi
. Then, create a new class that inherits from the IActionFilter
interface. This interface requires that you wire up two methods: OnActionExecuting
and OnActionExecuted
. The former event fires right before the endpoint action method is executed and the second one fires after the action method has run. As a learning exercise, let us modify the behavior of the example weatherforecast
endpoint that you receive in the webapi template, replacing the random weather forecasts with our own awesome forecast.
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Filterations { public class MyAwesomeFilter : IActionFilter { public void OnActionExecuted(ActionExecutedContext context) { Console.WriteLine("Im in ActionExecutedContext"); // Add awesome weather forecast var awesomeWeather = new WeatherForecast { Date = DateTime.Now, Summary = "Awesomeness", TemperatureC = 22 }; context.Result = new ObjectResult(awesomeWeather); } public void OnActionExecuting(ActionExecutingContext context) { Console.WriteLine("Im in ActionExecutingContext"); } } }
Now, to wire this up, go to the Program.cs class and use the optional options object when adding controllers to the pipeline.
using Filterations; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers( options => { options.Filters.Add(new MyAwesomeFilter()); }); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
Run your app and hit the weatherforecast
endpoint. It will return your new awesome weather forecast, every single time.
Filter Attribute
While the example above allows you to intercept all incoming requests and indiscriminately apply this filter, if you want to selectively apply the filter to only select endpoints, you’ll want to convert it to a filter attribute. You can do so by inheriting from the Attribute class.
Now that you have converted this filter as an attribute, you can decorate controllers or action methods of your choosing with this annotation and ASP.NET will selectively apply this filter to just those endpoints. Note the example below where the filter attribute is only applied to the GetWeatherForecast endpoint.
using Microsoft.AspNetCore.Mvc; namespace Filterations.Controllers; [ApiController] [Route("example")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [Route("weather")] [HttpGet(Name = "GetWeatherForecast")] [MyAwesomeFilter] // I'm selectively applying the filter to *only this* action method public IEnumerable<WeatherForecast> GetWeatherForecast() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } [Route("random")] [HttpGet(Name = "GetRandomNumber")] public int GetRandomNumber() { var randomNumber = new Random().Next(0, 1000000); return randomNumber; } }
After converting the filter to an attribute and decorating select endpoints with this attribute, also remember to remove the global wiring of this filter that you added as an option to the AddControllers()
in Program.cs, reverting back to the non-overloaded version.
var builder = WebApplication.CreateBuilder(args); // revert back to the non-overloaded version of the method builder.Services.AddControllers(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
Test the weather
endpoint and you’ll see the same output from before, the one that we are generating in the filter.
However, if you invoke the random
endpoint, it will return you a random number (not returning the ouput from the filter).
Closing Thoughts
Filters can be a powerful tool in your toolkit when building ASP.NET web applications. It can eliminate extra lines of code that you would otherwise have to write in multiple endpoints and instead allow you to centralize that logic in a single place. Besides the ActionFilter that we saw in the examples here, go try out some of the other types of filters on your own and see where you can incorporate these concepts in your own applications and architectures.