Middleware is a fundamental concept in ASP.NET that enables developers to process requests and responses in a modular pipeline. With the release of .NET 9, middleware continues to play a crucial role in shaping modern web applications. In this blog, we will explore middlewares in ASP.NET, beginning with a comparison of middleware in .NET Framework, .NET Core, and .NET, followed by an understanding of delegates and their relevance to RequestDelegate
, and finally, a step-by-step guide to building custom middleware.
Middleware Approaches: .NET Framework vs .NET Core vs .NET
Feature | .NET Framework | .NET Core (.NET 1-5) | .NET 6+ (.NET 9) |
Middleware Concept | HTTP Modules/Pipeline | Middleware Pipeline | Minimal APIs & Middleware |
Configuration | Global.asax , HttpModules , HttpHandlers | Startup.cs (Configure method) | Program.cs (Minimal Hosting Model) |
Performance | Slower due to tightly coupled request processing | Lightweight and modular | Further optimised with native AOT |
Flexibility | Limited due to rigid architecture | High flexibility with DI support | Improved API routing and performance |
Understanding Delegates and the Action Keyword
A delegate in C# is a type that represents references to methods with a specific signature. It allows methods to be passed as parameters, enabling flexible and extensible designs. A delegate is essentially a function pointer that is type-safe.
Example of a Delegate
public delegate void MyDelegate(string message);
public class Program
{
public static void PrintMessage(string message)
{
Console.WriteLine(message);
}
public static void Main()
{
MyDelegate del = PrintMessage;
del("Hello from delegate!");
}
}
While delegates are custom types, C# provides built-in generic delegate types like Action<>
, Func<>
, and Predicate<>
.
Action<T>
: Represents a method that takes a parameter and returns void.Func<T, TResult>
: Represents a method that takes parameters and returns a value.example:
Func<int, int, int> add = (int a, int b) => a + b; add(5, 7) // returns 12
Predicate<T>
: Represents a method that takes a parameter and returns a boolean.
Example of an Action
Action<string> printMessage = Console.WriteLine;
printMessage("Hello using Action!");
The key difference between a delegate and Action
is that a delegate is explicitly defined with a signature, whereas Action
is a predefined delegate that simplifies function references without requiring a custom delegate definition.
How does this relate to ASP.NET middleware? In ASP.NET, middleware is built using a RequestDelegate
, which is a delegate that processes HTTP requests.
public delegate Task RequestDelegate(HttpContext context);
It is the foundation of middleware in ASP.NET. Middlewares are executed sequentially, where each middleware component receives an HttpContext
, performs processing, and then optionally passes control to the next middleware in the pipeline.
Example of Using RequestDelegate
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) => {
Console.WriteLine("Middleware 1: Before Request");
await next.Invoke();
Console.WriteLine("Middleware 1: After Request");
});
app.Run(async context => {
await context.Response.WriteAsync("Hello from .NET Middleware!");
});
app.Run();
In this example, the first middleware logs messages before and after the request processing, and the Run
middleware handles the final response.
Building a Custom Middleware in .NET 9
To create a custom middleware, follow these steps:
Step 1: Create a Middleware Class
public class ExecutionTimeMiddleware {
private readonly RequestDelegate _next;
public ExecutionTimeMiddleware(RequestDelegate next) {
_next = next;
}
public async Task InvokeAsync(HttpContext context) {
var stopwatch = Stopwatch.StartNew();
await _next(context);
stopwatch.Stop();
Console.WriteLine($"Request processed in {stopwatch.ElapsedMilliseconds} ms");
}
}
Step 2: Register Middleware in Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseMiddleware<ExecutionTimeMiddleware>();
app.Run(async context => {
await context.Response.WriteAsync("Response from the App");
});
app.Run();
The IApplicationBuilder
interface is the standard way to register middleware in a structured and reusable manner. Instead of directly adding middleware in Program.cs
, we can create an extension method.
Step 3: Create an Extension Method
public static class ExecutionTimeMiddlewareExtensions {
public static IApplicationBuilder UseExecutionTimeLogger(this IApplicationBuilder builder) {
return builder.UseMiddleware<ExecutionTimeMiddleware>();
}
}
Step 4: Register Middleware Using the Extension
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseExecutionTimeLogger();
app.Run(async context => {
await context.Response.WriteAsync("Response from the App");
});
app.Run();
By using IApplicationBuilder
, we ensure a standardised approach to middleware registration, improving readability and maintainability.
Terminating Middleware
A terminating middleware is a middleware component in ASP.NET that does not call the next
delegate in the request pipeline. Instead, it processes the request and generates a response immediately, preventing any further middleware from executing.
Characteristics of Terminating Middleware:
It does not invoke
await next.Invoke();
It short-circuits the request pipeline by sending a response directly.
Typically implemented using
app.Run
()
instead ofapp.Use()
.
Example of Terminating Middleware:
app.Run(async context =>
{
await context.Response.WriteAsync("This is a terminating middleware. No further processing will occur.");
});
In this case:
Since there is no
next.Invoke()
call, no middleware registered after this will run.The request is handled entirely by this middleware.
Non-Terminating Middleware Example:
app.Use(async (context, next) =>
{
Console.WriteLine("Middleware 1: Before Request");
await next.Invoke();
Console.WriteLine("Middleware 1: After Request");
});
Here, await next.Invoke();
ensures the next middleware in the pipeline executes, making it non-terminating.
Conclusion
Middleware is a powerful feature in ASP.NET, enabling modular and efficient request processing. Understanding delegates and RequestDelegate
provides a foundation for grasping how middleware functions. With .NET 9, middleware has evolved to offer greater performance and flexibility. By following the steps outlined, developers can create custom middleware to suit their application’s needs.