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.
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 β
- How to build custom ASP.NET Core middleware from scratch
- How to reuse an incoming
X-Correlation-IDor generate a newGuidwhen 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
HttpContextmocking
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": ... }
| 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 |
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
- .NET 8 SDK or later
- Any IDE: Visual Studio 2022+, VS Code, or JetBrains Rider
# 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}/swaggerpublic 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);
}
}// Register early β before routing and endpoints
app.UseMiddleware<CorrelationIdMiddleware>();[HttpGet("status")]
public IActionResult GetStatus()
{
var correlationId = HttpContext.Items["CorrelationId"]?.ToString();
return Ok(new
{
status = "healthy",
correlationId,
timestamp = DateTime.UtcNow
});
}dotnet test CodingDroplets.RequestCorrelationMiddleware.slnUnit tests verify:
- Middleware generates a new correlation ID when none is provided
- Middleware reuses the incoming
X-Correlation-IDheader value - Response always contains the
X-Correlation-IDheader HttpContext.Items["CorrelationId"]is populated correctly
| 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 |
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.
- Write custom ASP.NET Core middleware β Microsoft Learn
- Logging and tracing in distributed systems β Microsoft Learn
- W3C Trace Context specification
This project is licensed under the MIT License.
| 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!