Skip to content

codingdroplets/dotnet-request-correlation-middleware

Repository files navigation

Request Correlation Middleware in ASP.NET Core

Traceable APIs across microservices β€” Custom middleware that stamps every request with an X-Correlation-ID, propagates it through the response, and makes distributed debugging actually possible.

.NET ASP.NET Core License: MIT Visit CodingDroplets YouTube Patreon Buy Me a Coffee GitHub


πŸš€ Support the Channel β€” Join on Patreon

If this sample saved you time, consider joining our Patreon community. You'll get exclusive .NET tutorials, premium code samples, and early access to new content β€” all for the price of a coffee.

πŸ‘‰ Join CodingDroplets on Patreon

Prefer a one-time tip? Buy us a coffee β˜•


🎯 What You'll Learn

  • How to build custom ASP.NET Core middleware from scratch
  • How to reuse an incoming X-Correlation-ID or generate a new Guid when one isn't provided
  • How to propagate the correlation ID back in the response headers for client-side tracing
  • How to expose the correlation ID in API response payloads for easy log correlation
  • How to unit test middleware behaviour with HttpContext mocking

πŸ—ΊοΈ Architecture Overview

Client Request
  (with or without X-Correlation-ID header)
        β”‚
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         CorrelationIdMiddleware  ← EARLY             β”‚
β”‚                                                      β”‚
β”‚  Has X-Correlation-ID header?                        β”‚
β”‚  YES β†’ reuse the incoming ID                         β”‚
β”‚  NO  β†’ generate new Guid.NewGuid()                   β”‚
β”‚                                                      β”‚
β”‚  β†’ Store in HttpContext.Items["CorrelationId"]        β”‚
β”‚  β†’ Add to response: X-Correlation-ID header          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              API Controller / Endpoint               β”‚
β”‚  Reads CorrelationId from HttpContext.Items           β”‚
β”‚  Includes it in the response payload                 β”‚
β”‚  Logs it with Serilog / ILogger for traceability     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚
        β–Ό
HTTP Response
  Headers: X-Correlation-ID: <guid>
  Body:    { "correlationId": "<guid>", "data": ... }

πŸ“‹ Middleware Behaviour

Scenario Behaviour
Request with X-Correlation-ID header Middleware reuses the provided ID
Request without X-Correlation-ID header Middleware generates a new Guid
All responses X-Correlation-ID header is always returned
API endpoints Correlation ID available via HttpContext.Items

πŸ“ Project Structure

dotnet-request-correlation-middleware/
β”œβ”€β”€ CodingDroplets.RequestCorrelationMiddleware.sln
β”œβ”€β”€ CodingDroplets.RequestCorrelationMiddleware.Api/
β”‚   β”œβ”€β”€ Middleware/
β”‚   β”‚   └── CorrelationIdMiddleware.cs    # Custom middleware implementation
β”‚   β”œβ”€β”€ Controllers/
β”‚   β”‚   └── SampleController.cs          # Demo endpoint returning correlation ID
β”‚   β”œβ”€β”€ Program.cs                       # Middleware registration
β”‚   └── CodingDroplets.RequestCorrelationMiddleware.Api.csproj
β”œβ”€β”€ CodingDroplets.RequestCorrelationMiddleware.Tests/
β”‚   β”œβ”€β”€ CorrelationIdMiddlewareTests.cs  # Unit tests for middleware behaviour
β”‚   └── CodingDroplets.RequestCorrelationMiddleware.Tests.csproj
β”œβ”€β”€ CHANGELOG.md
└── LICENSE

πŸ› οΈ Prerequisites

  • .NET 8 SDK or later
  • Any IDE: Visual Studio 2022+, VS Code, or JetBrains Rider

⚑ Quick Start

# Clone the repo
git clone https://github.com/codingdroplets/dotnet-request-correlation-middleware.git
cd dotnet-request-correlation-middleware

# Run the API
dotnet run --project CodingDroplets.RequestCorrelationMiddleware.Api

# Open Swagger UI β†’ http://localhost:{port}/swagger

πŸ”§ How It Works

Step 1 β€” Implement the Middleware

public class CorrelationIdMiddleware
{
    private const string CorrelationIdHeader = "X-Correlation-ID";
    private readonly RequestDelegate _next;

    public CorrelationIdMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        // Reuse incoming ID or generate a new one
        var correlationId = context.Request.Headers.TryGetValue(CorrelationIdHeader, out var incoming)
            ? incoming.ToString()
            : Guid.NewGuid().ToString();

        // Make it available downstream
        context.Items["CorrelationId"] = correlationId;

        // Always return it in the response
        context.Response.OnStarting(() =>
        {
            context.Response.Headers[CorrelationIdHeader] = correlationId;
            return Task.CompletedTask;
        });

        await _next(context);
    }
}

Step 2 β€” Register in Program.cs

// Register early β€” before routing and endpoints
app.UseMiddleware<CorrelationIdMiddleware>();

Step 3 β€” Use in Your Endpoints / Controllers

[HttpGet("status")]
public IActionResult GetStatus()
{
    var correlationId = HttpContext.Items["CorrelationId"]?.ToString();

    return Ok(new
    {
        status = "healthy",
        correlationId,
        timestamp = DateTime.UtcNow
    });
}

πŸ§ͺ Running Tests

dotnet test CodingDroplets.RequestCorrelationMiddleware.sln

Unit tests verify:

  • Middleware generates a new correlation ID when none is provided
  • Middleware reuses the incoming X-Correlation-ID header value
  • Response always contains the X-Correlation-ID header
  • HttpContext.Items["CorrelationId"] is populated correctly

πŸ€” Key Concepts

Why Correlation IDs Matter in Distributed Systems

Without Correlation IDs With Correlation IDs
"Which log entry matches the client error?" β†’ impossible Every log line tagged with the same ID
Debugging cross-service failures takes hours Grep by ID across all service logs
Client has no way to report a specific request Client can include the ID in a support ticket
No audit trail for individual requests Full request lifecycle traceable

Reuse vs Generate

Always reuse the incoming ID if provided β€” this allows a gateway, load balancer, or upstream service to stamp the ID before it reaches your API, maintaining a consistent ID across the entire call chain.


πŸ“š References


πŸ“„ License

This project is licensed under the MIT License.


πŸ”— Connect with CodingDroplets

Platform Link
🌐 Website https://codingdroplets.com/
πŸ“Ί YouTube https://www.youtube.com/@CodingDroplets
🎁 Patreon https://www.patreon.com/CodingDroplets
β˜• Buy Me a Coffee https://buymeacoffee.com/codingdroplets
πŸ’» GitHub http://github.com/codingdroplets/

Want more samples like this? Support us on Patreon or buy us a coffee β˜• β€” every bit helps keep the content coming!

About

ASP.NET Core Web API sample for request tracing with X-Correlation-ID propagation, reusable middleware, Swagger testing, and beginner-friendly clean structure.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages