Understanding .NET Middleware is essential for leveraging the full power of .NET CORE, a robust and adaptable framework for creating dynamic web apps. There are several organized functionalities that .NET CORE provides to ease the development process for developers.
One of the key features that .NET CORE provides is Middleware. Middleware intercepts the incoming request and outgoing response.
But what does this interception mean? Basically, it means that Middleware acts as a gatekeeper. It intercepts requests as they come into the application and responses as they leave. By this interception capability, developers can modify both incoming requests and outgoing responses as per the application’s requirements.
We can think of middleware as a piece of code that will be executed before any request goes to the application. Let’s say we want to check certain parameters from the request to check before it goes to the application, we can do that by using .NET Middleware.
If you need expert assistance in building efficient .NET applications, you can hire a .NET developer from Vivasoft Limited to help streamline your development process.
How Middleware Works
Understanding .NET Middleware requires knowledge of the pipeline model. Basically the pipeline model means that there may be several middleware in the application and if a request comes to the application, the request will go through every middleware in an order.
Let’s say we have 3 middleware in our application and we have order them like
app.UseMiddleware();
app.UseMiddleware();
app.UseMiddleware();
So, If a request comes to the application , the request will first go through ‘Middleware 1 to Middleware 3”. After passing through all the middleware it then reaches the application.
Ordering in middleware is very important. You can have a look at the figure below to understand it better.
Ways to Create Middleware
There are multiple ways to add middleware in the application. .NET Middleware also involves knowing the default middleware. Microsoft has introduced some default middleware to use in the application. Like Authentication middleware, CORS middleware, Routing middleware etc.
We can also create custom middleware as per our needs. There are 2 ways to create middleware.
- In-line Middleware
- Class Based Middleware
Inline Middleware
Inline middleware enables developers to write middleware logic directly to program.cs file (on Startup). Developers can write any type of logic.
It eliminates the complexity to write separate classes for middleware logic. Understanding .NET Middleware involves knowing how to use inline middleware effectively.
// Inline middleware for logging requests
app.Use(async (context, next) =>
{
// Log the request method and path
Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");
// Call the next middleware in the pipeline
await next.Invoke();
});
app.Run(async (context) =>
{
// Log response status code
Console.WriteLine($"Response Status Code : {context.Response.StatusCode}");
});
In .NET, both app.Use() and app.Run() are used for defining inline middleware. The key difference lies in their functionalities. The app.Use() method is used to add middleware to the request pipeline. It holds the reference for the next pipeline to execute.
If we need to add a middleware within the middleware pipeline, we use app.Use(). The next middleware of the middleware pipeline will start executing when next.Invoked() is called.
app.Run() acts as the final endpoint in the pipeline. It concludes the pipeline’s execution. It does not hold the reference for next middleware. In our above example no middleware will be executed after app.Run().
Class Based Middleware
Class based middleware gives more control over organizing and structuring application code. By this approach we add middleware as a class which provides an organized and structured way to handle http requests. This approach is a crucial part of Understanding .NET Middleware.
In this approach we simply don’t write any middleware code in the program.cs file but we write that in a separate class file and add that in the middleware pipeline in the program.cs file.
Have a look at the below class based middleware example. We are adding a custom header in response here. This will return data with header value ‘X-Response-Time-in-Milliseconds’ and Server-Name.
public class CustomMessageMiddleware
{
private readonly RequestDelegate _next;
public CustomMessageMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// add custom message in response header
// we will add a custom header in the response
// we can add multiple header in the response
var stopwatch = Stopwatch.StartNew();
// we are adding a delay of 2 seconds
Thread.Sleep(2000);
context.Response.Headers.Append("X-Response-Time-in-Milliseconds", stopwatch.ElapsedMilliseconds.ToString());
context.Response.Headers.Append("Server-Name", "Demo");
await _next(context);
}
}
Adding the reference into the program.cs file.
app.UseMiddleware();
Passing Data Between Middleware
.NET Middleware is not complete without learning how to pass data between them. Middleware works in a pipeline model.
When working with middleware, there will be a need to pass data between them. Data will flow from one middleware to another by order. There are multiple ways to do this.
One of the most widely used methods to pass data between middleware is HttpContext.Items. HttpContext.Items is a simple dictionary collection. We can store value here with a key-value pair.
The lifetime of data stored in HttpContext.Items is scoped to the current request. This means that it will only be available for the current request. Once the request is processed and data is sent back as response, the HttpContext class will be disposed of and data stored in HttpContext.Items will be lost.
public class Middleware1
{
private readonly RequestDelegate _next;
public Middleware1(RequestDelegate request)
{
_next = request;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine("Started Middleware 1");
context.Items["message"] = "Message from Middleware 1";
await _next(context);
}
}
public class Middleware2
{
private readonly RequestDelegate _next;
public Middleware2(RequestDelegate request)
{
_next = request;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine("Started Middleware 2");
var previousMessage = context.Items["message"] as string;
Console.WriteLine($"{previousMessage}");
await _next(context);
}
}
Here we are passing data from Middleware1 to Middleware2 by adding that in context.Items dictionary by the key of message. Middleware2 retrieves the data by the same key.
Conclusion
.NET Middleware provides a flexible way to handle requests. Effectively using middleware and data sharing techniques between them, developers can build robust applications that will have an organized and maintainable codebase.