Middleware in .NET: The Security Guards at Your API's Gate | Blogs by JaiD

Middleware in .NET: The Security Guards at Your API's Gate

March 14, 2026

Ever noticed how when you visit someone at a corporate office or a gated society, there's a guard who checks your ID, then a receptionist who makes you sign the register, and then someone who gives you a visitor pass before you can actually go in? Your HTTP request goes through a similar process before it reaches your API controller. That pipeline of checks and processing? That's middleware 🚪

I've been working with .NET for a while now, and honestly, I used middleware for months without truly understanding what was happening under the hood. I just knew app.UseAuthentication() had to come before app.UseAuthorization() because Stack Overflow told me so. But once I actually understood the pipeline, everything clicked. Let me save you from my months of confusion :p

What is Middleware?

In the simplest terms, middleware is a series of components that form a pipeline. Each HTTP request passes through every middleware component in order, and each component can:

  1. Do something with the request (log it, modify headers, check authentication)
  2. Pass it along to the next middleware
  3. Short-circuit the pipeline (reject the request before it even reaches the controller)

Think of it like an assembly line at a factory. Each station does one specific job, and the product moves down the line. Except here, the "product" is your HTTP request and the "stations" are middleware components.

Request → [Logging] → [Authentication] → [Authorization] → [CORS] → [Your Controller]
                                                                            ↓
Response ← [Logging] ← [Authentication] ← [Authorization] ← [CORS] ← [Your Controller]

The cool part? The response travels back through the same pipeline in reverse. So middleware can also modify the response on its way out. Two for the price of one 🎉

The Built-in Middleware You're Already Using

If you've set up a .NET web API, you've already used middleware without realizing it:

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

app.UseHttpsRedirection();    // Middleware 1: Redirect HTTP to HTTPS
app.UseAuthentication();       // Middleware 2: Who are you?
app.UseAuthorization();        // Middleware 3: Are you allowed here?
app.MapControllers();          // The actual endpoint

app.Run();

Each app.Use___() call adds a middleware to the pipeline. And the order matters. A LOT. You can't authorize a user before authenticating them, right? That's like checking someone's VIP pass before checking their ID. Doesn't make sense 🤷‍♂️

Writing Your Own Middleware

Here's where it gets fun. Let's say you want to log how long each request takes. You could sprinkle Stopwatch code across every controller, but that's messy. Instead, let's build a middleware that handles it for ALL requests.

The Inline Way (Quick and Dirty)

app.Use(async (context, next) =>
{
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
    
    await next(context); // Call the next middleware
    
    stopwatch.Stop();
    Console.WriteLine($"{context.Request.Method} {context.Request.Path} took {stopwatch.ElapsedMilliseconds}ms");
});

Drop this before your app.MapControllers() and boom — every request gets timed. The await next(context) part is crucial, that's what passes the request to the next middleware in the pipeline. Without it, the request just dies right there. Like a security guard who never opens the gate 💀

The Proper Way (Class-Based Middleware)

For production code, you'd create a proper middleware class:

public class RequestTimingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestTimingMiddleware> _logger;

    public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

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

        await _next(context); // Pass to next middleware

        stopwatch.Stop();
        _logger.LogInformation(
            "{Method} {Path} completed in {ElapsedMs}ms with status {StatusCode}",
            context.Request.Method,
            context.Request.Path,
            stopwatch.ElapsedMilliseconds,
            context.Response.StatusCode);
    }
}

And register it in your pipeline:

app.UseMiddleware<RequestTimingMiddleware>();

Or if you want to be fancy, create an extension method:

public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder app)
    {
        return app.UseMiddleware<RequestTimingMiddleware>();
    }
}

Now you can just write app.UseRequestTiming(); and it reads like poetry. Well, programmer poetry at least 📝

Short-Circuiting: When the Guard Says "Entry Nahi Milega"

Sometimes you want to stop a request dead in its tracks. Maybe it's missing a required header, or the API key is invalid. This is called short-circuiting.

public class ApiKeyMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        if (!context.Request.Headers.TryGetValue("X-Api-Key", out var apiKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("API Key is missing. Nice try though 😏");
            return; // Short-circuit! Don't call next()
        }

        // Key exists, let them through
        await _next(context);
    }
}

Notice the return without calling _next(context). The request never reaches the controller. It's like the security guard checking your visitor slip and saying "aapka naam list mein nahi hai" before you even get past the gate.

The Order Puzzle: Why Sequence Matters

This is probably the most common mistake I see (and made myself). The order you register middleware determines the order they execute. Here's the recommended order from Microsoft:

app.UseExceptionHandler();     // 1. Catch exceptions from everything below
app.UseHsts();                 // 2. HTTP Strict Transport Security
app.UseHttpsRedirection();     // 3. Redirect HTTP → HTTPS
app.UseStaticFiles();          // 4. Serve static files (short-circuits if found)
app.UseRouting();              // 5. Figure out which endpoint to hit
app.UseCors();                 // 6. Cross-Origin Resource Sharing
app.UseAuthentication();       // 7. Who are you?
app.UseAuthorization();        // 8. Can you do this?
app.MapControllers();          // 9. Actually run the endpoint

Swap UseAuthentication and UseAuthorization? Your auth won't work. Put UseStaticFiles after UseAuthorization? Now people need to log in just to see your CSS. Debugging middleware order issues is one of those things that makes you question your career choices 😅

Real-World Use Cases

Here are some middleware I've written or used in production:

  • Request/Response logging — Log every request for debugging (careful with sensitive data though!)
  • Correlation ID injection — Add a unique ID to every request so you can trace it across microservices
  • Rate limiting — Block users who are hammering your API too hard
  • Exception handling — Catch all unhandled exceptions and return a clean error response instead of a stack trace
  • Tenant resolution — In multi-tenant apps, figure out which tenant the request belongs to

Conclusion

Middleware is one of those concepts that seems intimidating at first but is actually pretty straightforward once you get the mental model right. It's just a pipeline of components that process requests and responses in order. Each component does its job and passes things along (or doesn't, if there's a problem).

The key takeaways:

  • Middleware runs in the order you register it — order matters
  • Each middleware can process both the request (going in) and response (coming out)
  • You can short-circuit the pipeline at any point
  • The next() delegate is what connects one middleware to the next

Next time someone asks "why isn't my auth working?", check your middleware order first. 9 times out of 10, that's the answer :p


Profile picture

Written by Jai Kumar Dewani is a Software Engineer at Carl Zeiss, who loves to learn and build cool things. Follow me @jai_dewani