Middleware, Filter and Attributes.

 




Examples: 


Attributes

Aspect

Filter

Middleware

Attribute

Purpose

Execute custom logic at specific points during the MVC request pipeline (e.g., before/after actions).

Handle requests and responses globally or for specific routes; used for cross-cutting concerns.

Add metadata to code elements for various purposes, often used with reflection.

Scope

Specific to MVC actions, controllers, or globally across MVC actions.

Applied globally or to specific routes in the application.

Applied to classes, methods, properties, etc.

Execution Stage

Executes before and/or after action methods, before and/or after results, or when exceptions occur.

Executes during the request-processing pipeline; can short-circuit the pipeline.

Executes at compile time or runtime, depending on how metadata is used.

Configuration

Configured in the MVC pipeline (e.g., via Startup.cs / (Program.cs v6 or later) or attribute-based).

Configured in the Startup.cs / (Program.cs v6 or later) file.

Configured by applying the attribute directly to code elements.

Can Short-Circuit

No

Yes, middleware can short-circuit the pipeline and handle requests/responses directly.

No, attributes cannot short-circuit request processing.

Common Use Cases

Logging, validation, exception handling, and action result modification.

Authentication, logging, exception handling, request/response modification.

Metadata-driven behavior, such as serialization settings or validation rules.







Middleware






Filter






  1. Middleware





Lets create a Custom Middleware, We will create logging middleware which logs requests and outgoing responses 



There are multiple way to write code in middleware but in this we will using extension method to create our middleware


app.use(), app.map() and aap.run() these are the methods to configure request delegates


The first Run delegate is always terminal and terminates the pipeline. Run is a convention.


Don't write response after next.Invoke method only do logging and other stuff

If another Use or Run delegate is added after the Run delegate, it's not called.






Order of middleware: It is important to put middleware in the right order to achieve expected behavior, security and performance.



Our custom middleware sits after authorization , check the order before adding custom middleware, it should be in proper order



Filter is another topic we will learn it as well after middleware

The order is critical for security, performance, and functionality.




In above image code you can see we created our custom Middleware, Now we can add it in Programe.cs file 


We can directly register our middleware using app.UseMiddleware<LoggingMiddleware>(); method or we can wrap it in static class i.e extension method so we can use it as app.useLoggig();




OR

Create a static class and register our middleware in extension method  in IApplicationBuider




Now use our middleware in programe.cs file



Custom Exception Middleware 











  1. Filter



Filter Pipelines



Filters: 


What is filter 

When to use and where to use 

Use cases of filter 

How to create and register custom filter 

Built-in filters 




In the above image you can see Filters and how they execute in specific order by endpoint middleware.


In ASP.NET Core, filters allow you to execute code before or after specific stages in the request processing pipeline. These stages are:


Authorization: Runs before the other stages to determine whether the current user is authorized to access the requested resource.


Resource: Executes after authorization but before model binding. It is used to make decisions based on the request context or modify the request.


Action: Executes code before and after the action method is called. These are the most commonly used filters and are typically applied to perform actions such as logging, validation, or modifying the action result.


Exception: Runs only when an unhandled exception occurs during the request processing. It can be used to handle errors and return custom error responses.


Result: Runs before and after the execution of the action result. It can be used to modify the action result or to perform actions such as logging the result or modifying the HTTP response.



Lets create and register Custom middleware 



  1. Create a custom Filter by implementing IActionFilter 

  2. Register filter in Program.cs file or using Service filter on controller level



We have to remember IActionFilter interface whenever we want to create custom filter, We have to create a class (eg. loggerFilter) and we have to implement IActionFilter interface in LoggerFilter class 



Visual studio intelligent system will show the error when you implement IActionFilter, to resolve we have to implement IActionFilter’s method in loggerFilter class we can use VS intel. Feature to automatically implement the method, like in below images.






Logger Filter code 



Lets globally register this filter so we can use it for every controller, Open Program.cs file add the below code there 


Globally Register it in AddController method of IServiceCollection 



To test it run code in debug mode and put breakpoint in filter, it will stop the code there 



Register Filter on controller level using ServiceFIlter 





Controller or Method (Locally)  specific Register of logger filter 

And update our Program.cs file again, We have to remove globally register LoggerFilter and register LoggerFilter in DI container 


  1. Register it in Services(DI)



Remove or comment globally on the register filter.




  1. Apply Filter on Controller or Method 




Or Method 



OR 



So this is how we can use filters in our application to handle cross-cutting concerns.






  1.  Attributes


Attributes allow you to add metadata into code. It is used to decorate classes, methods, properties and fields etc..  to provide additional information that can be accessed on runtime.


There are many built-in attributes available in .net.






Lets create custom attribute: To do so we have to derive new class from Attribute class, 


  1. Create Custom Attribute using Attribute base class



  1. Apply this Custom Attribute on Method.



In above we apply the description attribute and decorate it on method, this attribute used to describe about method or other element of code eg. class field etc. 



We can also get this information  using Reflector.




Let's implement a role-based authorization system using Attribute and Action filters. This will be a high-level overview but can be easily scalable.

Prerequisites:

  1. Filter: Check if the user has permission to access the resource.

  2. Attribute: Set metadata on the controller's method.

Step-by-Step Implementation:

  1. Create a Custom Attribute:

    • This attribute will set the "Allow Access" metadata on the controller's method when annotated.

  2. Create a Custom Filter:

    • This filter will check if the user has the proper permissions to access the resource.

Example:

  • User Peter logs into the system and has the Order:Read permission. This means Peter can only access the Order API methods that have read access, but cannot access actions like Order:Write or Order:Delete.

  • We have a GetOrder method annotated with [AllowAccess("Order:Read")]. This attribute indicates that only users with the Order:Read permission can access this method.

  • We use JWT for authentication. When Peter logs in, he is provided with a JWT token with a claim set to Order:Read, which means Peter can access the GetOrder method.

  • We have a CheckAccessFilter that verifies whether the user has the appropriate permission. If the user has permission, access to the resource is granted; otherwise, a forbidden access exception is thrown.










Now Register the CheckAccessFilter globally in Program.cs file




Use AllowAccess Attribute on Method.




Let's run the code and test it.



First Check user without Order:Read Permission 


User peter has no Permission Claim, Try to access Order method with it.


Expected result : Forbidden




Now check User with Permission Order:Read, let's give peter Order:read permission




User has Permission claim now check the api again

Expected Response , hence our code is working fine. 




The above is for learning to set up RBAC , Instead of using IActionFilter we can IAuthorizationFilter  or Directly write our code inside Attribute as well, Or we can use ActionFilterAttribute class 


We will test one by one the above method approaches. 


  1. Instead of using Attribute and Action filter to check Permission we will use a single custom class to handle this using ActionFilterAttribute this will be used as Attribute see image.




  1. We can also use IAuthorizationFilter to handle RBAC check below code image.





Or use TypeFilter 



Program.cs file 




We have multiple approaches to handle this use case, each with its benefits.

  1. Using IAuthorizationFilter Interface and AllowAccess Attribute:

    • This approach allows us to manage authorization and permission access within a single class.

  2. Using ActionFilterAttribute:

    • With this approach, we don't need to create an AllowAccess attribute separately; we can define the access logic within a single class.

    • However, this approach does not support dependency injection (DI). If you need to inject dependencies, this method is not suitable. Otherwise, it is efficient and allows managing code in one place.

  3. Using IActionFilter and AllowAccess Attribute:

    • If you want to use dependency injection and prefer not to use IAuthorizationFilter, you can opt for IActionFilter combined with the AllowAccess attribute.



To understand the order and interaction of Middleware and Filters, you can place debugger breakpoints in your middleware, filter, and controller levels. Then, you can perform step-by-step debugging (F10) to observe how they work.


Useful Built-in Attributes


  • [Authorize]: Specifies that access to a controller or action method is restricted to users who meet the authorization requirement.

  • [AllowAnonymous]: Allows anonymous users to access a controller or action method, even if [Authorize] is applied globally or to the controller.

  • [Route]: Defines a route template for a controller or action method.

  • [HttpGet] ,[HttpPost] , [HttpPut("{id}")] , [HttpDelete("{id}")] , [HttpPatch("{id}")]

  • Model Binding and Validation [BindProperty]

  • [FromBody]: Binds a parameter to the body of the request.

  • [FromQuery]: Binds a parameter to the query string.

  • [FromRoute]: Binds a parameter to a route value.

  • [FromHeader]: Binds a parameter to a request header.

  • [FromForm]: Binds a parameter to form data.

  • [Required]: Specifies that a data field is required.

  • [StringLength]: Specifies the minimum and maximum length of a string data field.

  • [ApiController]: Specifies that a controller responds to web API requests. This attribute makes attribute routing a requirement, automatically validates model state, and provides other API-specific behaviors.

  • [Produces]: Specifies the type of response an action returns.

  • [ProducesResponseType]: Specifies the type of response and HTTP status code returned by an action.

  • [ValidateAntiForgeryToken]: Validates the anti-forgery token in requests.

  • [Consumes]: Specifies the type of request an action accepts.

  • [NonAction] attribute. When you have private methods or any methods in a controller that you do not want to expose as actions, you can use the [NonAction] attribute.

  • [ResponseCache]: Sets caching parameters for responses.






Useful built-in Filters



ExceptionFilterAttribute: A base class for creating custom exception filters.

ResultFilterAttribute: A base class for creating custom result filters that run code before and after the action result executes.



Useful built-in Middleware


Asdfasdf


Static Files Middleware Serves static files such as HTML, CSS, images, and JavaScript.

Authentication Middleware Enables authentication capabilities.

Authorization Middleware Enforces authorization policies.

Routing Middleware Matches request URLs to route handlers.

Endpoint Middleware Executes the matched endpoint.

CORS Middleware Configures Cross-Origin Resource Sharing.

HTTPS Redirection Middleware Redirects HTTP requests to HTTPS.

HSTS Middleware Adds HTTP Strict Transport Security headers.


Developer Exception Page Middleware Provides detailed error information during development.


Exception Handling Middleware Catches exceptions and generates error responses.


Status Code Pages Middleware Generates responses for HTTP status codes like 404, 500, etc.


Response Compression Middleware Compresses response data.


Response Caching Middleware Caches responses.


Session Middleware Enables session state.


Request Localization Middleware Configures localization for the request.


Forwarded Headers Middleware Forwards headers from reverse proxies.


WebSockets Middleware Enables WebSocket protocol.


File Server Middleware Serves static files and enables directory browsing.


Cookie Policy Middleware Manages cookie settings and consent.


Health Checks Middleware Provides application health checks.



Comments