Custom Middleware

Back

Loading concept...

Custom Middleware in ASP.NET Core

The Security Guard Story

Imagine you’re at a big amusement park. Before you can go on any ride, you have to pass through security guards standing at different checkpoints.

Each guard has ONE special job:

  • 🎫 First guard checks your ticket
  • 📏 Second guard checks your height
  • 🎒 Third guard checks your bag

Only after ALL guards say “OK!” can you enjoy the rides!

This is exactly how middleware works in ASP.NET Core!


What is Custom Middleware?

Think of it like this:

Every web request is like a person walking through a hallway. Middleware is like the doors in that hallway. Each door can:

  1. Check something about you
  2. Let you pass to the next door
  3. Or stop you completely!

Custom Middleware = You build your OWN special door with YOUR rules!

graph TD A["Request Arrives"] --> B["Middleware 1"] B --> C["Middleware 2"] C --> D["Your Custom Middleware"] D --> E["Your App"] E --> F["Response Goes Back"]

Two Ways to Create Custom Middleware

Just like there are two ways to make a sandwich (quick way or chef way), there are two ways to create middleware:

Method Best For Difficulty
Convention-Based Simple, quick tasks Easy
IMiddleware Interface Complex, DI-heavy tasks Medium

Method 1: Convention-Based Middleware (The Quick Way)

This is like writing a quick note. You just need a class with a special method!

The Recipe:

  1. Create a class
  2. Accept RequestDelegate in constructor
  3. Write an Invoke or InvokeAsync method

Simple Example:

public class HelloMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(
        HttpContext context)
    {
        // Do something BEFORE
        Console.WriteLine("Hello!");

        await _next(context);

        // Do something AFTER
        Console.WriteLine("Goodbye!");
    }
}

What’s Happening Here?

Think of _next as the “next door” in our hallway:

Part What It Does
_next Points to next middleware
InvokeAsync Runs when request arrives
await _next(context) Opens the next door

Registering Your Middleware

You built the door. Now you need to put it in the hallway!

// In Program.cs
app.UseMiddleware<HelloMiddleware>();

Or create a nice extension method:

public static class HelloExtensions
{
    public static IApplicationBuilder
        UseHello(
            this IApplicationBuilder app)
    {
        return app.UseMiddleware
            <HelloMiddleware>();
    }
}

// Now use it like this:
app.UseHello();

Method 2: IMiddleware Interface (The Pro Way)

This is like following a strict recipe book. You implement a special interface!

Why Use IMiddleware?

Feature Convention-Based IMiddleware
Lifetime Singleton (one instance) Per-request
DI in method Manual injection Automatic
Best for Simple tasks Complex services

The Recipe:

public class LoggingMiddleware
    : IMiddleware
{
    private readonly ILogger _logger;

    public LoggingMiddleware(
        ILogger<LoggingMiddleware> logger)
    {
        _logger = logger;
    }

    public async Task InvokeAsync(
        HttpContext context,
        RequestDelegate next)
    {
        _logger.LogInformation(
            "Request: {Path}",
            context.Request.Path);

        await next(context);

        _logger.LogInformation(
            "Response: {Code}",
            context.Response.StatusCode);
    }
}

IMiddleware Registration (Important!)

With IMiddleware, you MUST register it as a service:

// Step 1: Register as service
builder.Services
    .AddTransient<LoggingMiddleware>();

// Step 2: Use in pipeline
app.UseMiddleware<LoggingMiddleware>();

Forget Step 1? You’ll get an error!

graph TD A["Create IMiddleware Class"] --> B["Register in Services"] B --> C["Add to Pipeline"] C --> D["Works!"] A --> E["Skip Registration"] E --> F["Error!"]

Real-World Example: Request Timer

Let’s build something useful! A middleware that tracks how long each request takes:

public class TimerMiddleware
    : IMiddleware
{
    private readonly ILogger _log;

    public TimerMiddleware(
        ILogger<TimerMiddleware> log)
    {
        _log = log;
    }

    public async Task InvokeAsync(
        HttpContext ctx,
        RequestDelegate next)
    {
        var watch = Stopwatch
            .StartNew();

        await next(ctx);

        watch.Stop();

        _log.LogInformation(
            "{Path} took {Time}ms",
            ctx.Request.Path,
            watch.ElapsedMilliseconds);
    }
}

The Middleware Order Matters!

Remember our security guards? Order matters!

If the height-checker comes before the ticket-checker, things get weird!

// This order...
app.UseAuthentication();
app.UseAuthorization();
app.UseMyCustomMiddleware();

// ...is different from this!
app.UseMyCustomMiddleware();
app.UseAuthentication();
app.UseAuthorization();
graph TD A["Request"] --> B["First Middleware"] B --> C["Second Middleware"] C --> D["Third Middleware"] D --> E["Your Controller"] E --> D D --> C C --> B B --> F["Response"]

Short-Circuiting: Stopping Early

Sometimes a guard says “STOP! You can’t enter!”

Your middleware can do this too:

public async Task InvokeAsync(
    HttpContext context)
{
    if (context.Request.Path == "/blocked")
    {
        context.Response.StatusCode = 403;
        await context.Response
            .WriteAsync("Not allowed!");
        return; // Don't call _next!
    }

    await _next(context);
}

Notice: We don’t call _next. The request stops here!


Quick Comparison Table

Feature Convention-Based IMiddleware
Signature InvokeAsync(HttpContext) InvokeAsync(HttpContext, RequestDelegate)
Constructor Gets RequestDelegate Gets DI services
Lifetime Singleton Per-request
DI Support Method injection Constructor injection
Registration Just UseMiddleware Service + UseMiddleware

Common Mistakes to Avoid

Mistake Problem Fix
Forgetting await _next() Request never completes Always await next
Wrong order Auth fails Check order carefully
No service registration Runtime error Add AddTransient for IMiddleware

Key Takeaways

  1. Middleware = Doors in your request hallway
  2. Convention-based = Quick and easy
  3. IMiddleware = Better for complex DI
  4. Order matters = First registered, first executed
  5. Short-circuit = Return early to stop the request

You Did It!

Now you understand custom middleware! You can:

  • Create your own security guards for requests
  • Choose between convention-based and IMiddleware
  • Register and order them correctly
  • Stop requests when needed

Go build something awesome!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.