Creating Custom Middleware in .NET Core 8: A Step-by-Step Guide

Middleware is a powerful feature in ASP.NET Core that allows you to intercept HTTP requests and responses in your application. It provides a way to add custom logic to handle various aspects of request processing, such as logging, authentication, or error handling. In this blog post, we’ll walk through the process of creating a custom logging middleware in .NET Core 8.

What is Middleware?

Middleware is a component that is executed on every request within the ASP.NET Core request pipeline. It can inspect, route, modify, or even short-circuit requests and responses. Middleware is added to the pipeline in a specific order, and each piece of middleware can perform its own task before passing control to the next component.

Types of Middleware and When to Use Them

Middleware can be categorized based on its functionality and the stage in the request-response cycle when it operates. Here are some common types of middleware and their use cases:

Request Logging Middleware:
  • Purpose: Logs details about incoming requests and outgoing responses.
  • When to Use: Useful for debugging, monitoring, and auditing. It helps track what happens during request processing.
  • Example: Logging the request path, query parameters, headers, and response status.
Authentication Middleware:
  • Purpose: Handles user authentication, ensuring that only authorized users can access certain resources.
  • When to Use: Required for applications with security needs, such as login systems or protected APIs.
  • Example: JWT authentication or OAuth middleware.
Authorization Middleware
  • Purpose: Ensures that authenticated users have the necessary permissions to access specific resources.
  • When to Use: After authentication, to enforce role-based or policy-based access control.
  • Example: Middleware that checks if a user has the required role or claim.
Error Handling Middleware:
  • Purpose: Catches exceptions that occur during request processing and generates a proper response.
  • When to Use: To provide a user-friendly error message or log errors for further analysis.
  • Example: Custom error pages or global exception handling.
Static File Middleware:
  • Purpose: Serves static files such as HTML, CSS, JavaScript, and images.
  • When to Use: For serving static content in web applications.
  • Example: Middleware that serves files from the wwwroot folder.
CORS Middleware
  • Purpose: Handles Cross-Origin Resource Sharing (CORS) to control how resources can be shared across different origins.
  • When to Use: For APIs that need to be accessed from different domains or client applications.
  • Example: Allowing or blocking requests from specific domains.
Compression Middleware:
  • Purpose: Compresses HTTP responses to reduce bandwidth usage.
  • When to Use: To improve the performance of applications by reducing the size of responses.
  • Example: Gzip or Brotli compression.
Creating the Middleware

Let’s create a custom middleware that logs request and response details, including request method, path, query string, and response status code.

Step 1: Set Up Your Project

First, create a new ASP.NET Core Web API project if you don’t have one already. Open your terminal and run:

dotnet new webapi -n MiddlewareExample
cd MiddlewareExample
Step 2: Implement the Middleware

Create a Middleware Folder: Create a new folder named Middleware in the root of your project.

Add Middleware Class: Inside the Middleware folder, add a new file named RequestResponseLoggingMiddleware.cs and add the following code:

// Middleware/RequestResponseLoggingMiddleware.cs
using Microsoft.AspNetCore.Http;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        
        // Log Request
        var request = context.Request;
        request.EnableBuffering();
        var reader = new StreamReader(request.Body);
        var requestBody = await reader.ReadToEndAsync();
        request.Body.Position = 0;

        Console.WriteLine($"Request: {request.Method} {request.Path} {request.QueryString}");
        Console.WriteLine($"Request Body: {requestBody}");

        // Copy original response body stream
        var originalBodyStream = context.Response.Body;

        using (var newBodyStream = new MemoryStream())
        {
            context.Response.Body = newBodyStream;

            // Continue processing the request
            await _next(context);

            // Log Response
            context.Response.Body.Seek(0, SeekOrigin.Begin);
            var responseBody = await new StreamReader(context.Response.Body).ReadToEndAsync();
            context.Response.Body.Seek(0, SeekOrigin.Begin);

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

            // Copy response back to the original body stream
            await newBodyStream.CopyToAsync(originalBodyStream);
        }
        
        stopwatch.Stop();
        Console.WriteLine($"Request Processing Time: {stopwatch.ElapsedMilliseconds} ms");
    }
}
Step 3: Register the Middleware

To use your custom middleware, you need to add it to the request pipeline in the Program.cs file.

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();

var app = builder.Build();

// Add middleware to the request pipeline
app.UseMiddleware<RequestResponseLoggingMiddleware>();

app.UseAuthorization();

app.MapControllers();

app.Run();
Step 4: Test the Middleware

Run your application:

dotnet run

Use a tool like Postman or swagger to send requests to your API. Check the output in the debug console to see the logged request and response details.

Middleware Registration in .NET Core
  • Direct Middleware Registration: Simple and inline, suitable for quick implementations.
  • Using Middleware Extensions: Cleaner approach for managing middleware registration.
  • Middleware as a Service (IMiddleware): Integrates with dependency injection, suitable for more complex middleware.
  • Middleware with Options: Allows configuration via options patterns.
  • Using Middleware in ASP.NET Core Modules: Encapsulates middleware within specific modules for modular applications.
Conclusion

Creating custom middleware in .NET Core 8 allows you to insert your own logic into the request processing pipeline. In this example, we built a middleware component that logs request and response details, but the possibilities are endless. Middleware can be used for a variety of tasks such as authentication, error handling, and more.

By understanding how to create and use middleware, you can tailor your application’s behavior to fit specific needs and ensure a more robust and maintainable codebase.

Feel free to explore more about middleware and experiment with different functionalities to enhance your ASP.NET Core applications! Add your comments or share your experiences with custom middleware in the comments below. What unique uses or challenges have you encountered?

Thanks for reading! Cheers and happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *