DEV Community

Cover image for .NET 6 - Web API Correlation Id 🔗
Mohamad Lawand
Mohamad Lawand

Posted on

.NET 6 - Web API Correlation Id 🔗

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
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
        }
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

The next step is attach the services to the application startup in the program.cs

builder.Services.AddCorrelationIdGeneratorService();
Enter fullscreen mode Exit fullscreen mode

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;
        });
        }
}
Enter fullscreen mode Exit fullscreen mode

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>();
}
Enter fullscreen mode Exit fullscreen mode

And we need to update the program.cs with the following

app.AddCorrelationIdMiddleware();
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

Please comment any of your questions and thank you for reading.

Top comments (2)

Collapse
 
abu7midan profile image
abu7midan

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

Collapse
 
iria profile image
Iria

link with github repo is broken