ASP.NET Core Middleware

Middleware is the backbone of the ASP.NET Core request processing pipeline. It's a component that plugs into the application pipeline and has access to the request and response objects. Middleware components can execute code, call the next middleware in the pipeline, and modify the request and response as they flow through the pipeline.

Key Concept: Think of the middleware pipeline as a series of steps that a request must pass through before reaching the application's core logic and before the response is sent back to the client.

The Request Pipeline

In ASP.NET Core, the request pipeline is configured in the Startup.cs file within the Configure method. This method takes an IApplicationBuilder and an IWebHostEnvironment as arguments.

The IApplicationBuilder interface provides methods to:

The order in which you add middleware is crucial. The pipeline executes in the order the middleware is configured.

Common Middleware Components

ASP.NET Core provides a rich set of built-in middleware components:

Example Configuration in Startup.cs


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"));
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts(); // HTTP Strict Transport Security
    }

    app.UseHttpsRedirection(); // Redirect HTTP requests to HTTPS

    app.UseStaticFiles(); // Serve static files

    app.UseRouting(); // Enable routing

    app.UseAuthentication(); // Enable authentication
    app.UseAuthorization(); // Enable authorization

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllers(); // For API controllers
    });
}
            

Important: The order of UseRouting() and UseEndpoints() is fixed. UseRouting() must come before any middleware that depends on routing information (like UseAuthorization()), and UseEndpoints() must come after those, as it executes the endpoint chosen by routing.

Creating Custom Middleware

You can create your own custom middleware to encapsulate specific logic. There are several ways to do this:

1. Using an Anonymous Function (Inline Middleware)

This is useful for simple, single-purpose middleware.


app.Use(async (context, next) =>
{
    // Code to execute before the next middleware
    await context.Response.WriteAsync("Hello from custom middleware! ");
    await next(); // Call the next middleware in the pipeline
    // Code to execute after the next middleware has finished
    await context.Response.WriteAsync(" Returning from custom middleware.");
});
            

2. Using a Class with a Invoke or InvokeAsync Method

This is more organized for complex logic.


// CustomMiddleware.cs
public class CustomMiddleware
{
    private readonly RequestDelegate _next;

    public CustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Logic before next middleware
        await context.Response.WriteAsync("

Before Custom Middleware

"); await _next(context); // Call the next middleware // Logic after next middleware await context.Response.WriteAsync("

After Custom Middleware

"); } } // Startup.cs - Configure method app.UseMiddleware<CustomMiddleware>();

3. Using an Extension Method

This is the most common and recommended approach for making custom middleware reusable and discoverable.


// MiddlewareExtensions.cs
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CustomMiddleware>();
    }
}

// Startup.cs - Configure method
app.UseMyCustomMiddleware();
            

Tip: Consider using dependency injection to inject services into your custom middleware's constructor if it needs to access other parts of your application.

Middleware Order and Execution Flow

The order of middleware in the Configure method defines how requests are processed and how responses are formed.

  1. A request enters the pipeline at the first middleware configured.
  2. Each middleware can perform actions before or after calling the next delegate.
  3. If a middleware doesn't call next, it effectively short-circuits the pipeline for that request.
  4. The request continues down the pipeline until it reaches the final destination (e.g., a controller action or Razor Page).
  5. The response then travels back up the pipeline, with each middleware having a chance to process it.

Conclusion

Understanding and effectively using ASP.NET Core middleware is fundamental to building robust and scalable web applications. By composing middleware, you can implement cross-cutting concerns such as authentication, logging, error handling, routing, and more, in a clean and modular fashion.