ASP.NET Core  

Understanding Middleware in ASP.NET Core with Examples

Introduction

Middleware is a software component that processes HTTP requests and responses in the ASP.NET Core pipeline.

Each request passes through a series of middleware components before reaching the final endpoint (like a controller or API). After the endpoint processes the request, the response travels back through the same middleware pipeline.

Request Flow

Client Request
     ↓
Middleware 1 (Logging)
     ↓
Middleware 2 (Authentication)
     ↓
Middleware 3 (Routing)
     ↓
Controller / API
     ↑
Response travels back through middleware

Each middleware can:

  • Inspect or modify the request

  • Call the next middleware in the pipeline

  • Modify the response

  • Stop the pipeline if needed

Basic Middleware Example

Let’s see a simple middleware example.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Use(async (context, next) =>
{
    Console.WriteLine("Request started");

    await next();

    Console.WriteLine("Response completed");
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello World");
});

app.Run();

Execution Flow

  • A request arrives.

  • Middleware prints Request started.

  • next() passes the request to the next middleware.

  • Run() sends the response.

  • Control returns to the previous middleware.

  • Middleware prints Response completed.

Console Output:

Request started
Response completed

Understanding Use Middleware

Use() middleware is used when we want to perform logic before and after the next middleware.

Example

app.Use(async (context, next) =>
{
    Console.WriteLine("Before next middleware");

    await next();

    Console.WriteLine("After next middleware");
});

Key Characteristics

  • Can execute code before and after the next middleware

  • Must call next() to pass control

This is commonly used for:

  • Logging

  • Request validation

  • Performance monitoring

Understanding Run Middleware

Run() is a terminal middleware.

Once Run() executes, the pipeline stops and no other middleware is executed after it.

Example

app.Run(async context =>
{
    await context.Response.WriteAsync("Pipeline ended here");
});

If multiple Run() methods are written:

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello");
});

app.Run(async context =>
{
    await context.Response.WriteAsync("World");
});

Output:

Hello

The second Run() will never execute.

Understanding Map Middleware

Map() is used to branch the middleware pipeline based on URL paths.

Example

app.Map("/admin", adminApp =>
{
    adminApp.Run(async context =>
    {
        await context.Response.WriteAsync("Admin page");
    });
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Main page");
});

Result

URLResponse
/Main page
/adminAdmin page

This approach is useful when we want different middleware pipelines for different routes.

Creating Custom Middleware

In real applications, middleware is usually created as a separate class.

Step 1: Create Middleware Class

public class LoggingMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task Invoke(HttpContext context)
    {
        Console.WriteLine("Request received");

        await _next(context);

        Console.WriteLine("Response sent");
    }
}

Step 2: Register Middleware

app.UseMiddleware<LoggingMiddleware>();

Now every request will pass through this middleware.

Built-in Middleware in ASP.NET Core

ASP.NET Core provides several built-in middleware components.

Common examples include:

MiddlewarePurpose
UseRoutingDetermines the endpoint for a request
UseAuthenticationIdentifies the user
UseAuthorizationChecks access permissions
UseStaticFilesServes static files
UseExceptionHandlerHandles global errors

Example configuration:

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

Key Takeaways

  • Middleware forms the HTTP request pipeline in ASP.NET Core.

  • Each middleware component can process requests and responses.

  • Use() allows execution before and after the next middleware.

  • Run() is terminal middleware and stops the pipeline.

  • Map() creates branch pipelines based on URL paths.

  • Custom middleware helps implement logging, validation, monitoring, and security.

Conclusion

Middleware is one of the most powerful features in ASP.NET Core. By structuring the request pipeline effectively, developers can handle cross-cutting concerns like authentication, logging, and error handling in a clean and maintainable way.

Understanding middleware and how to create custom components is essential for building scalable and maintainable web APIs.