When building APIs, handling HTTP requests typically requires multiple steps, such as authentication, logging, error handling and more.
Rather than repeating logic across multiple parts of the system, middlewares allow you to process requests in a maintainable way.
Middleware offers a centralized mechanism for managing cross-cutting concerns within an application by intercepting and processing
Middleware offers a centralized mechanism for managing cross-cutting concerns within an application by intercepting and processing HTTP requests and responses in a pipeline.
The middleware pipeline is a sequence of middlewares that are executed one after the other.
Each middleware handles its specific concern and then passes the request to the next middleware in the pipeline.
Once the response is generated, it travels back through the previous middlewares in reverse order.
This structure means that each middleware can perform tasks both before and after the next middleware, which makes the order of execution crucial.
For instance, middleware that handles exceptions should be placed early in the pipeline to catch any errors that may occur later.
.NET provides many built-in middleware components that you might find useful in your projects. Here are a few that I regularly use:
However, built-in middleware is often not enough, so we frequently need to create custom middleware. .NET provides three ways to implement custom middleware:
Delegate-based middleware is the simplest way to define middleware in ASP.NET Core. It allows you to define inline logic directly in the request pipeline using app.Use():
app.Use(async (context, next) =>
{
if (!context.Request.Headers.ContainsKey("X-Required-Header"))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("Missing X-Required-Header");
return;
}
await next.Invoke();
});
In this example, our custom middleware checks if incoming HTTP requests contain the required header.
If the header is missing, the middleware will set response status code to 400, if the request includes required header, the middleware forwards the request to the next step in the pipeline.
With convention-based middleware, you can create middleware by following predefined conventions.
To create one, your class should inject a RequestDelegate and implement an InvokeAsync method that takes an HttpContext as an argument:
public class ValidateHeaderConventionMiddleware
{
private readonly RequestDelegate _next;
public ValidateHeaderConventionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (!context.Request.Headers.ContainsKey("X-Required-Header"))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("Missing X-Required-Header");
return;
}
await _next.Invoke(context);
}
}
Once the middleware is defined, you need to use the UseMiddleware method to register it with your application:
app.UseMiddleware<ValidateHeaderConventionMiddleware>();
Lastly, my preferred approach to creating middlewares, factory-based middlewares.
Factory-based middlewares rely on the IMiddleware interface, which includes the InvokeAsync method with HttpContext and RequestDelegate as parameters.
public class ValidateHeaderFactoryMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (!context.Request.Headers.ContainsKey("X-Required-Header"))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("Missing X-Required-Header");
return;
}
await next.Invoke(context);
}
}
If you choose this approach, you must first register the middleware in the DI container and then add it to your application’s pipeline:
builder.Services.AddTransient<ValidateHeaderFactoryMiddleware>();
app.UseMiddleware<ValidateHeaderFactoryMiddleware>();
Middleware simplifies the handling of HTTP requests by centralizing cross-cutting concerns.
The middleware pipeline processes requests in sequence, with the execution order determined by the registration sequence.
.NET provides many built-in middlewares to address common requirements. When custom middleware is needed, you can create it using one of three approaches.
If you want to check out examples I created, you can find the source code here:
I hope you enjoyed it, subscribe and get a notification when a new blog is up!
Your email address will not be published. Required fields are marked *