Back to: ASP.NET Core Web API Tutorials
Filters in ASP.NET Core Web API
In this article, I will briefly introduce you to Filters in an ASP.NET Core Web API application. Filters in ASP.NET Core Web API allow you to execute custom code before or after specific stages in the request processing pipeline. They help manage cross-cutting concerns, such as logging, exception handling, authentication, and more, without cluttering the main action methods.
What Are Filters in ASP.NET Core Web API?
Filters in ASP.NET Core Web API are reusable components (attributes or classes) that allow developers to execute custom logic before or after an action method is invoked in a controller. Filters help decouple concerns such as validation, logging, security, and exception handling, making the code more modular and easier to maintain.
Think of filters as security checkpoints at an airport. Before a passenger (HTTP request) can board a plane (controller action), they go through several checkpoints (filters), each performing a focused task, such as checking documents (authentication), scanning baggage (validation), and inspecting the boarding pass (authorization). Filters act as these checkpoints, handling tasks before or after the main business logic (action method) runs. For a better understanding, please have a look at the following diagram:
Why Do We Need Filters in ASP.NET Core Web API?
Filters allow us to separate concerns like logging, validation, exception handling, and security into distinct components so that we don’t have to repeat these checks in each controller action. By encapsulating these tasks in filters, we keep our action methods focused on business logic, improve code readability, and reduce duplication.
Imagine you are running a library, and you want everyone to sanitize their hands before entering. Instead of repeating the instruction to each visitor, you set up a sanitization station (filter) at the entrance, ensuring everyone sanitizes automatically. Similarly, filters reduce repetitive code by centralizing checks like validation or logging across multiple actions, which helps to keep the code focused on the business logic and makes it more maintainable.
We have already discussed that when a client makes a request, that request comes to the Routing Engine, which then navigates that Request to the Controller Action Method. So, the Controller action method will handle the incoming request and send the response back to the client who initially made the request, as shown in the image below.
But what will you do if you want to execute some code or logic before or after the action method is executed, as shown in the image below?
If that is your requirement, then you need to use Filters in your ASP.NET Core Web API application.
Types of Filters in ASP.NET Core Web API
ASP.NET Core provides several types of filters, each serving a specific purpose:
1. Authorization Filters
Authorization filters run first in the filter pipeline. They handle security concerns such as ensuring that the request is authorized to proceed. These filters are primarily used to authenticate the user and check if the user has the necessary permissions (roles, claims, etc.) to access a specific resource or endpoint.
- How It Works: The Authorization Filter verifies whether the user is authenticated and authorized. For example, it checks whether the user has a valid token and if the user’s role matches the required role to access the resource.
- Example: The [Authorize] attribute is a built-in authorization filter that is applied to controller actions or controllers themselves to ensure only authenticated and authorized users can access certain endpoints.
- Real-time Use Case: In an admin dashboard API endpoint, you can apply the [Authorize(Roles = “Admin”)] filter to ensure that only users with the “Admin” role can access the endpoint. If a user without the “Admin” role tries to access it, the filter will prevent them from doing so and return a 403 Forbidden response.
2. Action Filters
Action filters execute before and after an action method runs. They are typically used for tasks such as logging, modifying inputs or outputs, or measuring performance. They allow you to add logic before or after the execution of the action method.
- How It Works: Action Filters run at two points during the request. Before the action method is executed, they allow you to modify input data or perform validation. After the action method has completed, they allow you to modify the action result or log the output.
- Example: An action filter that logs every incoming request and its parameters for auditing purposes.
- Real-time Use Case: For example, in an e-commerce application, you can log every user activity by using an action filter. Each time a user performs an action like adding an item to the cart or making a purchase, an action filter can log the request and parameters into a log file or database for auditing purposes.
3. Result Filters
Result filters execute after the action method has run but before the response is sent to the client. They are useful for modifying or logging the final response, such as adding headers, modifying the body, or compressing large response payloads. The result filter provides a final opportunity to alter the response before it’s sent to the client.
- How It Works: The Result Filter is executed after the action method has returned its result. It allows you to modify the result before it is sent out, such as adding custom headers, changing the response content, or compressing the response data.
- Example: A result filter that compresses large JSON data before returning it to the client.
- Real-time Use Case: Suppose you are building an API that returns a list of products. If the list is very large, you can apply a result filter to compress the response to make it more efficient for network transfer. For example, you could apply gzip compression to the response before sending it to the client to reduce the payload size.
4. Exception Filters
Exception filters catch and handle any unhandled exceptions thrown by action methods. They are used to manage errors in a centralized way, returning custom error messages or logging the error details. Exception filters allow you to intercept exceptions and provide user-friendly error responses.
- How It Works: Exception Filters are triggered when an unhandled exception is thrown during the execution of an action method. You can catch these exceptions and decide how to handle them, such as logging the error or returning a custom error message to the client.
- Example: An exception filter that catches a database connection failure and returns a custom error message.
- Real-time Use Case: If a user submits an invalid ID in the request body, and the action method attempts to query a database with that invalid ID, an exception filter can catch the resulting error. It could then return a 400 Bad Request status code along with a message detailing the issue (e.g., “Invalid product ID”).
5. Resource Filters
Resource filters run before all other filters in the pipeline. They are typically used for resource management tasks such as caching, authentication, or processing requests before reaching the action method. These filters are ideal for scenarios where early-stage resource management is required.
- How It Works: Resource Filters are executed before authorization filters and action filters. They are often used for tasks like caching results or managing resources, such as limiting the number of concurrent requests to a resource.
- Example: A resource filter that checks if data is cached and returns the cached data if available, preventing redundant processing or database calls.
- Real-time Use Case: In a weather forecast API, a resource filter could cache the results of weather data for a particular location. If a user makes a subsequent request for the same location within a certain time frame, the filter can return the cached data instead of querying the database again, thus improving performance and reducing database load.
Default Filter Execution Order in ASP.NET Core Web API
Filters in ASP.NET Core are executed in a well-defined order to ensure they interact correctly. For a better understanding, please have a look at the following diagram:
The following is the default execution order for each filter type:
- Authorization Filters: Authorization filters run first and ensure that the request is authenticated and authorized to proceed.
- Resource Filters: Resource filters run next and manage resources, such as checking if data should be cached or processed.
- Action Filters: Action filters run before and after the action method executes. They are typically used for logging, modifying inputs/outputs, or validation.
- Result Filters: Result filters run after the action completes, just before the response is sent to the client. They are used to modify or log the final response (e.g., adding headers or compressing the response).
- Exception Filters: Exception filters run last and handle any unhandled exceptions that occur during the execution of the action method.
How Do Filters Work in ASP.NET Core Web API?
Filters work by plugging custom logic into specific stages of the ASP.NET Core request pipeline. Let us understand how filters work with one real-time example. Please have a look at the following diagram:
Scenario: A User Trying to Access a Restricted API Resource
- Authorization Filter: Imagine the user is trying to access a secure admin page. The Authorization Filter first checks if the user is logged in and has the necessary ‘Admin’ role. If the user is not authorized, the filter will block the request and return a 403 Forbidden response.
- Resource Filter: Next, the Resource Filter checks if the data (e.g., a list of users) is cached. If it is, the filter serves the cached data, saving time and reducing database load.
- Action Filter: The Action Filter logs the request details, like which parameters were sent, and validates that the input data is correct before passing it to the action method.
- Result Filter: After the action method runs, the Result Filter checks if the response needs to be modified (e.g., adding custom headers, compressing large data).
- Exception Filter: Finally, if any exceptions were thrown during the process (e.g., database errors), the Exception Filter catches them and sends a custom error message.
Where Can We Apply Filters in ASP.NET Core Web API?
In ASP.NET Core Web API, filters can be applied at different levels based on the scope and requirements. These levels are globally, controller level, and action level. For a better understanding, please have a look at the following diagram:
Apply Filter Globally
When you apply a filter globally, it affects all controllers and action methods throughout the entire application. Global filters are typically used for concerns that apply universally across the application, such as authentication, logging, or exception handling.
- How It Works: Global filters are registered in the Program.cs file (or Startup.cs for older versions) to ensure that they run for every HTTP request. This is especially useful for tasks that must be enforced for every action, like authentication or logging of all requests.
- When to Use It: Apply a global filter when you need to enforce a rule or behavior across the entire application. For instance, if all requests to your Web API need to be authenticated, you would apply an authorization filter globally.
- Example: Suppose you want to ensure that every action in your application requires the user to be authenticated. You can apply the [Authorize] filter globally, ensuring all actions across all controllers check for user authentication before processing any request. This prevents unauthorized users from accessing any endpoint.
- Real-Time Use Case: Applying global logging filters to capture every request and log information like request parameters, execution time, and response status for all actions in the application.
Applying Filter at Controller Level
Filters applied at the controller level affect all actions within that controller. This level of filtering is used when you need specific behavior for a group of related actions within a controller.
- How It Works: When you apply a filter at the controller level, it runs for every action in that controller. This is ideal when certain functionality, like logging or validating data, should only apply to a specific set of actions within a controller.
- When to Use It: Use controller-level filters when you want to apply the filter to all actions in a controller, but not globally to the entire application. For example, you may want to log all actions within an AdminController but not for every action in the entire Web API.
- Example: Suppose you want to log every action in an AdminController, which contains sensitive or high-priority operations. You could apply a logging filter to the entire controller.
- Real-Time Use Case: Apply an authorization filter only to controllers related to sensitive operations (e.g., the AdminController), ensuring that only authorized users can access the admin actions.
Applying Filter at Action Level
Filters applied at the action level are the most specific and are only applied to a single action method within a controller. This is useful for specialized handling that only applies to one method, not the entire controller.
- How It Works: When applied to an action, the filter runs only when that particular action is invoked. This allows for more granular control over filtering behavior and is useful when only one or two specific actions need to execute additional logic (like error handling or input validation).
- When to Use It: Apply action-level filters when you need very specific behavior for a particular action, such as when one action needs exception handling or a different logging mechanism that doesn’t apply to other actions.
- Example: Imagine you have a DeleteProduct action in an e-commerce application. For this action, you need to apply a custom exception filter to handle errors like trying to delete a product that doesn’t exist. You would apply the exception filter only to that action.
- Real-Time Use Case: Apply a validation filter on a specific action that accepts sensitive or complex data, such as an action that processes payments, where you might want to perform extra validation on the input data.
What happens when the same Filter is applied at multiple levels?
If the same filter is applied at multiple levels (globally, at the controller, and at the action) in ASP.NET Core Web API, the filter will be executed multiple times, once for each level it’s applied, so, for example, a logging or authorization filter applied globally, on a controller, and on an action method will run three times during a single request to that action, potentially causing redundant or repeated operations unless you specifically design your filter to prevent duplicate execution.
Differences Between Filters and Middleware in ASP.NET Core Web API
In ASP.NET Core Web API, both middleware and filters are used to handle HTTP requests, but they serve different roles within the request processing pipeline. Here’s a clearer explanation and comparison:
Middleware
Middleware is a global component that is executed early in the request pipeline. It works across all HTTP requests and responses, meaning it can modify or inspect requests or responses for any action or controller in the application. Middleware is responsible for handling concerns that apply globally to the entire application, such as routing, security, exception handling, or modifying the response (e.g., adding headers).
How It Works: Middleware runs for every request coming into the system, and it is processed in the order in which it is registered. Each piece of middleware has access to the request, and it can either modify the request, pass it along the pipeline, or stop the request entirely (e.g., by returning a response early).
Example: Imagine you are managing a website with multiple pages (actions). A middleware component could check every visitor (HTTP request) for certain rules (authentication, headers, etc.) before allowing them to view any page (controller action). This happens before they even reach the actual page content (action method). You need to create a middleware that checks if a user is logged in and authenticated before allowing access to any page in the web application.
When to Use:
Middleware is ideal for tasks that need to be applied globally across all routes in your application. Common use cases include:
- Authentication and authorization
- CORS (Cross-Origin Resource Sharing)
- Logging requests
- Exception handling
- Modifying response headers (e.g., adding security headers)
Real-World Analogy:
Imagine a traffic signal on a road. Before any car (request) proceeds, it must stop at the traffic signal (middleware). The signal might let the car pass (allow the request), make the car wait (delay the request), or redirect it (change request headers) based on the global rules (e.g., checking if the car follows the road rules or is allowed to proceed). This process is applied to all cars on that road.
Filters
Filters are more action-specific components in the pipeline. They execute before or after an action method runs in the controller. Filters allow developers to apply logic that is specifically related to the execution of the action method, such as logging request parameters, validating model data, handling errors, or modifying the response after an action has been executed.
How They Work: Filters are triggered by specific actions and run either before or after the controller method is executed. They can be applied globally, at the controller level, or at the action level, giving you fine-grained control over when they run. Filters provide a way to handle concerns tied to individual actions or controllers without affecting other parts of the application.
Example: Now, imagine you want to log the details of a specific action like “Create User” in an admin dashboard. A filter could log the user inputs (parameters) and the output of the action method for debugging or audit purposes. You need to create an action filter that logs the parameters and the result of the CreateUser action.
When to Use:
Filters are ideal for handling cross-cutting concerns that should only apply to specific actions or controllers. Common use cases include:
- Action-specific validation
- Logging input parameters or output results for debugging
- Exception handling at the action level
- Authorization checks for specific actions
Real-World Analogy:Â
Think of filters as specific checkpoints along a road. At each checkpoint (filter), the car (request) might be checked for specific things, such as:
- Authorization checkpoint: Is the car registered (authorization filter)?
- Safety checkpoint: Does the car meet safety standards (action filter)?
- Final checkpoint: Is the car ready to exit the road (result filter)?
Unlike middleware, which is global, filters are targeted checks that only apply to specific parts of the journey (actions or controllers).
Key Differences Between Filters and Middleware in ASP.NET Core Web API:
Filters in ASP.NET Core allow for clean separation of concerns. Instead of placing repetitive logic in multiple places, we can centralize it in filters to improve code maintainability and readability. Filters also enable reusable cross-cutting functionality like logging, caching, and security management.
In the next article, I will discuss the Authorization Filters in an ASP.NET Core Web API Application. In this article, I explain the basic concepts of Filters in ASP.NET Core Web API Applications. I hope you enjoy this article.