Filters in ASP.NET Core
Filters act as a pipeline, similarly to middleware. They can be used both with MVC and Razor Pages. They can be applied to individual controllers, actions or globally, to all of them. Filters run as part of Endpoint middleware.
Comparison with the Middleware
Filters is like a small middleware. It hooks into the lifecycle of a request-response procedure.
- Middleware runs for ALL requests, filters run for selected requests (or globally as well).
- Filters have access to
HttpContextand MVC constructs such as
IActionResult. Middleware has access only to
Middleware is more general and it’s designed to solve concerns that are cross-cutting throughout the app.
There are 5 types of filters, each one running at a different stage (MVC):
- Authorization (request only) - short-circuits the request if it’s unauthorized
- Resource (2-ways) - they can have many use-cases, depending on needs. It runs before model binding.
- Action (2-ways) - runs after model binding. We can manipulate action’s arguments; or they can modify action’s response
- Exception - catch exceptions allowing us to prepare a better response. It catches exceptions in:
- model binding and validation
- actions execution
- action/page filter execution
- Result (2-way) - run before and after
IActionResultis executed, we can control that execution within a filter.
Filters registered globally will be executed for both MVC and Razor Pages.
Non-global filters are implemented as attributes. The order of attributes application dictates the order of filters execution.
Filters can be sync or async. Each filter type has two interfaces for that reason. For example, the Authorization filter has:
The synchronous filters that execute multiple times (twice or 3 times) have separate methods in their interface for each of the executions. The asynchronous ones have one method and delegates that we need to execute between request/response operations.
Here’s an example of a synchronous filter:
Here’s an example of an asynchronous filter:
Filters can be applied:
- to MVC:
- to Razor Pages
PageModel(not individual methods)
Both MVC and Razor Pages:
Just the Razor Pages:
Filters are executed in the following order for a request:
- global filters
- controller filters
- base controller filters
- action filters
We can change the default order of execution by implementing the
interface, which has just one property -
int Order. Lower value of
moves filter up in the queue. Filters with the same
Order execute using the
framework’s ordering rules, presented before.
Since filters are attributes (unless global), it’s not easy to inject services into them. Filters are singletons. One approach would be to use the service locator pattern, but nobody likes that.
Instead, a filter could be split into two classes:
- one implementing the
I*Filter- it would contain the DI and filter functionality
- another one implementing either
The solution is based on
IFilterFactory that ASP.NET Core “understands” and it
knows how to handle it.