ASP.NET Core 8 middleware request pipeline diagram with authentication, custom middleware, logging, and endpoint flow

What Is Middleware in ASP.NET Core 8? Request Pipeline Explained with Custom Middleware Example

What Is Middleware in ASP.NET Core?

common middleware in ASP.NET Core:

Understanding the ASP.NET Core Request Pipeline

Client → Authentication → Logging → Routing → Controller → Response → Logging → Client

How Middleware Works Internally

1. RequestDelegate

2. HttpContext

3. The next() Delegate

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        // You can add custom logic here before passing to next middleware
        Console.WriteLine("Request incoming: " + context.Request.Path);

        // Call the next middleware in the pipeline
        await _next(context);

        // You can also add logic after the next middleware has run
        Console.WriteLine("Response outgoing: " + context.Response.StatusCode);
    }
}

How to Create Custom Middleware in ASP.NET Core 8 (Step-by-Step)

Step 1: Create a Middleware Class

RequestLoggingMiddleware.cs
using System.Diagnostics;

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();

        Console.WriteLine($"Incoming Request: {context.Request.Method} {context.Request.Path}");

        // Call the next middleware in the pipeline
        await _next(context);

        stopwatch.Stop();

        Console.WriteLine($"Response Status Code: {context.Response.StatusCode}");
        Console.WriteLine($"Request completed in {stopwatch.ElapsedMilliseconds} ms");

        // Add custom header
        context.Response.Headers.Add("X-Custom-Header", "MiddlewareDemo");
    }
}

Step 2: (Optional but Recommended) Create an Extension Method

RequestLoggingMiddlewareExtensions.cs
public static class RequestLoggingMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestLoggingMiddleware>();
    }
}

Step 3: Register Middleware in Program.cs

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

// Register custom middleware
app.UseRequestLogging();

app.MapGet("/", () => "Hello World!");

app.Run();

Step 4: Run and Test the Application

dotnet run
https://localhost:5001/
Hello World!
Incoming Request: GET /
Response Status Code: 200
Request completed in 15 ms

How It Fits in the Request Pipeline

Request → Custom Middleware → Next Middleware → Endpoint
Response ← Custom Middleware ← Next Middleware ← Endpoint

Real-World Use Cases for Custom Middleware

Built-In Middleware in ASP.NET Core 8

app.UseRequestLogging();

1. Authentication Middleware

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://your-auth-server";
        options.Audience = "your-api";
    });

var app = builder.Build();

app.UseAuthentication();
Request → Authentication → Next Middleware
HttpContext.User
HttpContext.User

2. Authorization Middleware

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseAuthorization();
app.MapGet("/secure", () => "Secure Data")
   .RequireAuthorization();
Request → Authentication → Authorization → Endpoint

3. Routing Middleware

var app = builder.Build();

app.UseRouting();

app.MapGet("/", () => "Hello World!");
Request → Routing → Endpoint Selection → Execution

4. Exception Handling Middleware

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/error");
}
app.UseDeveloperExceptionPage();
Request → Exception Handling → Rest of Pipeline

5. Static Files Middleware

app.UseStaticFiles();
wwwroot/index.html
https://localhost:5001/index.html
Request → Static Files → (if file exists → return file)
                         → (if not → next middleware)

How Built-In Middleware Fits Together

app.UseExceptionHandler("/error");

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
ASP.NET Core middleware pipeline flow showing request and response processing through multiple middleware components

Middleware Order Matters

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
Request
   ↓
Routing
   ↓
Authentication (Who are you?)
   ↓
Authorization (Are you allowed?)
   ↓
Endpoint
   ↓
Response
app.UseRouting();

app.UseAuthorization();   // ❌ Wrong position
app.UseAuthentication();  // ❌ Comes too late

app.MapControllers();
Request
   ↓
Routing
   ↓
Authorization (User not authenticated yet!)
   ↓
Authentication
   ↓
Endpoint
403 Forbidden
app.UseAuthorization();
app.UseAuthentication();
app.UseAuthentication();
app.UseAuthorization();

Use vs Run vs Map in Middleware

1. app.Use() → Continues the Pipeline

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

    await next();  // Pass control to next middleware

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

2. app.Run() → Terminates the Pipeline

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

3. app.Map() → Conditional Branching

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

app.Run(async context =>
{
    await context.Response.WriteAsync("Public Page");
});
Request
   ↓
Is path "/admin"?
   ├── Yes → Admin Branch → Response
   └── No  → Continue Main Pipeline

Conclusion

Once you understand that flow, everything else becomes easier:

Recommended Next Reads

🚀 Subscribe for Tech Updates

📧

👤

We respect your privacy. Unsubscribe anytime.