In this article we will be cover Correlation Id in APIs and howe can add them to our .NET Web API and utilise them.
You can watch the full video on youtube:
And find the full source code on github:
https://github.com/mohamadlawand087/Api-Correlation
A Correlation ID is a unique identifier value that is attached to requests and messages that allow reference to a particular transaction or event chain. Attaching a Correlation ID to a request is arbitrary.
Create a new project
dotnet new webapi -n CorrerlationApi
Once we open it in VS Code, we need to add a new folder in the root directory called Configurations
Inside the Configurations folder we need to create a new folder called interfaces and inside the interface we need to create an interface called ICorrelationIdGenerator
namespace CorrerlationApi.Configurations.Interfaces;
public interface ICorrelationIdGenerator
{
string Get();
void Set(string correlationId);
}
Now inside the Configuration folder we need to create the CorrelationIdGenerator class
using ApiCorrolation.Configurations.Interfaces;
namespace CorrerlationApi.Configurations;
public class CorrelationIdGenerator : ICorrelationIdGenerator
{
private string _correlationId = Guid.NewGuid().ToString();
public string Get() => _correlationId;
public void Set(string correlationId) {
_correlationId = correlationId;
}
}
Since the CorrolationId would be generated per request, not across the lifetime of the application we need to inject the CorrelationIdGenerator as a Scoped service rather then a singleton one
To register the Correlation Id we can utilise an extension method so inside the Configuration folder we will create a new folder called Services
Inside the Services folder we create a new class called ServiceCollectionExtensions and we add the following
using CorrerlationApi.Configurations.Interfaces;
using CorrerlationApi.Configurations;
namespace CorrerlationApi.Services;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCorrelationIdGeneratorService(this IServiceCollection services)
{
services.AddScoped<ICorrelationIdGenerator, CorrelationIdGenerator>();
return services;
}
}
The next step is attach the services to the application startup in the program.cs
builder.Services.AddCorrelationIdGeneratorService();
Now that our CorrelationId services are able to be injected scoped, we need to create the middleware which is responsible for catching the incoming request, analysing it for correlation-id in the header
inside the root directory we create a new folder called Helpers and inside the Helpers folder we create a new class called CorrelationIdMiddleare.cs and we add the following
using CorrerlationApi.Configurations.Interfaces;
using Microsoft.Extensions.Primitives;
namespace CorrerlationApi.Helpers;
public class CorrelationIdMiddleware
{
private readonly RequestDelegate _next;
private const string _correlationIdHeader = "X-Correlation-Id";
public CorrelationIdMiddleware(RequestDelegate next) {
_next = next;
}
public async Task Invoke(HttpContext context, ICorrelationIdGenerator correlationIdGenerator)
{
var correlationId = GetCorrelationId(context, correlationIdGenerator);
AddCorrelationIdHeaderToResponse(context, correlationId);
await _next(context);
}
private static StringValues GetCorrelationId(HttpContext context, ICorrelationIdGenerator correlationIdGenerator)
{
if(context.Request.Headers.TryGetValue(_correlationIdHeader, out var correlationId))
{
correlationIdGenerator.Set(correlationId);
return correlationId;
}
else
{
return correlationIdGenerator.Get();
}
}
private static void AddCorrelationIdHeaderToResponse(HttpContext context, StringValues correlationId)
{
context.Response.OnStarting(() =>
{
context.Response.Headers.Add(_correlationIdHeader, new[] {correlationId.ToString()});
return Task.CompletedTask;
});
}
}
The next step is injecting the middleware in the application startup inside the Services folder we create a new class called ApplicationBuilderExtensions
using CorrerlationApi.Helpers;
namespace CorrerlationApi.Services;
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder AddCorrelationIdMiddleware(this IApplicationBuilder applicationBuilder)
=> applicationBuilder.UseMiddleware<CorrelationIdMiddleware>();
}
And we need to update the program.cs with the following
app.AddCorrelationIdMiddleware();
Lastly let us test the functionality in our controller, by updating the WeatherForcastController with the following
using CorrerlationApi.Configurations.Interfaces;
using CorrerlationApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace CorrerlationApi.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ICorrelationIdGenerator _correlationIdGenerator;
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(
ILogger<WeatherForecastController> logger,
ICorrelationIdGenerator correlationIdGenerator)
{
_logger = logger;
_correlationIdGenerator = correlationIdGenerator;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
_logger.LogInformation("CorrelationId {correlationId}: Processing weather forecast request",
_correlationIdGenerator.Get());
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
Please comment any of your questions and thank you for reading.
Top comments (2)
dear @moe23
thank you for that , really i appreciate , but for achieving the main goal of correlating the request
i recommend using
if get header correlation id == null
add new correlation
this will group one ore more operations in one id.
thanks
link with github repo is broken