.NET Core  

How to Create Custom Middleware in ASP.NET Core with Example?

Introduction

In modern web applications built using ASP.NET Core Web API, handling requests and responses efficiently is very important. Every request that comes to your application passes through a pipeline before reaching the controller and then goes back through the same pipeline as a response.

This pipeline is called the Middleware Pipeline.

Sometimes, built-in middleware is not enough, and you need custom logic like:

  • Logging requests

  • Handling exceptions

  • Validating headers

  • Modifying responses

This is where Custom Middleware in ASP.NET Core becomes very useful.

In this article, we will learn how to create custom middleware in ASP.NET Core step by step in simple words, with real-world examples and best practices.

What is Middleware in ASP.NET Core?

Middleware is a component that handles HTTP requests and responses.

πŸ‘‰ It sits in the request pipeline and can:

  • Process incoming requests

  • Modify request/response

  • Call the next middleware

  • Stop the request if needed

Simple Flow

Request β†’ Middleware 1 β†’ Middleware 2 β†’ Controller β†’ Middleware β†’ Response

πŸ‘‰ Each middleware decides whether to pass control to the next middleware.

Why Use Custom Middleware?

Sometimes you need logic that is not available by default.

1. Logging Requests and Responses

You can track every request coming into your API.

2. Global Exception Handling

Handle errors in one place instead of repeating try-catch everywhere.

3. Authentication or Header Validation

Check tokens or headers before processing the request.

4. Performance Monitoring

Measure how long each request takes.

πŸ‘‰ Custom middleware helps keep your code clean and reusable.

Types of Middleware in ASP.NET Core

1. Built-in Middleware

Examples:

  • UseRouting

  • UseAuthentication

  • UseAuthorization

2. Custom Middleware

Middleware created by developers to handle specific logic.

How to Create Custom Middleware in ASP.NET Core

Let’s implement a custom middleware step by step.

Step 1: Create Middleware Class

Create a new class:

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");

        await _next(context);

        Console.WriteLine($"Response Status: {context.Response.StatusCode}");
    }
}

Explanation

  • RequestDelegate β†’ Represents the next middleware

  • InvokeAsync β†’ Main method executed for every request

  • HttpContext β†’ Contains request and response data

πŸ‘‰ Always call _next(context) to continue the pipeline.

Step 2: Register Middleware in Program.cs

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

app.UseMiddleware<RequestLoggingMiddleware>();

app.MapControllers();

app.Run();

Important Note

Middleware order matters!

πŸ‘‰ The order you add middleware defines execution flow.

Step 3: Run and Test

When you call any API endpoint, you will see logs like:

  • Request: GET /api/products

  • Response Status: 200

πŸ‘‰ This confirms middleware is working.

Creating a Reusable Extension Method (Best Practice)

To keep Program.cs clean, use extension methods.

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

Use in Program.cs

app.UseRequestLogging();

πŸ‘‰ Cleaner and more readable code.

Real-World Example: Exception Handling Middleware

Let’s create a global exception handler.

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            context.Response.StatusCode = 500;
            context.Response.ContentType = "application/json";

            var response = new
            {
                message = "Something went wrong",
                detail = ex.Message
            };

            await context.Response.WriteAsJsonAsync(response);
        }
    }
}

Why This is Useful?

  • Centralized error handling

  • Cleaner controllers

  • Consistent error response

Middleware Execution Order in ASP.NET Core

Order is very important in ASP.NET Core Middleware Pipeline.

Example

app.UseMiddleware<ExceptionMiddleware>();
app.UseMiddleware<RequestLoggingMiddleware>();

πŸ‘‰ Exception middleware should come first to catch all errors.

Best Practices for Custom Middleware in ASP.NET Core

1. Keep Middleware Lightweight

Do not write heavy logic inside middleware.

2. Always Call Next Middleware

If you don’t call _next(context), request will stop.

3. Use Dependency Injection

Inject services like logging, database, etc.

4. Handle Exceptions Properly

Use try-catch for safe execution.

5. Maintain Proper Order

Execution depends on registration order.

Common Mistakes to Avoid

  • ❌ Forgetting to call _next(context)

  • ❌ Writing too much logic inside middleware

  • ❌ Wrong middleware order

  • ❌ Not handling exceptions

Real-World Use Cases

In real applications, middleware is used for:

  • Logging

  • Authentication

  • Authorization

  • Error handling

  • Request validation

πŸ‘‰ Almost every production-level ASP.NET Core Web API uses middleware.

Summary

Custom middleware in ASP.NET Core is a powerful way to handle cross-cutting concerns like logging, error handling, and request processing. By creating your own middleware, you can build clean, reusable, and scalable applications. Understanding how the middleware pipeline works and following best practices ensures that your application performs efficiently and remains easy to maintain.