When calling APIs from your website, you may wish to send headers and make other HTTP request modifications. In my case, I’d always want to ensure I send a specific correlation ID with each downstream request to any microservice.
When I create an HttpClient for the HttpFactory in the Dependency Injection Service Container, I don’t yet have access to the HttpContext of the incoming request. This is a bigger problem in APIs, where another application may be passing an existing correlation ID that I may want to grab and forward to any downstream requests.
On a website, I usually use a user identifier such as the user’s IdP unique ID. If I need or want to keep it more private, I just use the session ID.
Rather than add the correlation ID to each request in the repository classes, I want to have all transactions automatically check for an incoming correlation ID and, if so, use it as the correlation ID for downstream requests to any other APIs. If one doesn’t exist, then I’ll either generate one or use a different value.
To do this, I’ll create a special helper class to retrieve the value and override the SendAsync method to add it to any downstream request during the life of the current request.
/// <summary>
/// Handler for HTTP requests to propagate specific headers from the incoming request to outgoing requests
/// </summary>
public class RequestHeaderPropagationHandler(IHttpContextAccessor CurrentContext) : DelegatingHandler
{
/// <summary>
/// Handle the SendAsync Event
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Microsoft.AspNetCore.Http.HttpContext? httpContext = CurrentContext.HttpContext;
if (httpContext != null)
{
string _corId = httpContext.GetCorrelationID();
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("X-CORRELATION-ID", _corId);
}
return base.SendAsync(request, cancellationToken);
}
}
You’ll notice I have an Extension method called GetCorrelationID() that I use to see if the incoming request has one I want to use.
/// <summary>
/// Get the CorrelationID for this HttpContext or replies with
/// </summary>
/// <param name="value"></param>
/// <param name="newCorrID"></param>
/// <returns></returns>
public static string GetCorrelationID(this HttpContext value, string newCorrID = "")
{
string _corId = string.Empty;
StringValues header = value.Request.Headers["X-CORRELATION-ID"];
if (header.Count > 0)
{
_corId = header.First() + string.Empty;
}
// Use the new correlation ID if provided
if (!string.IsNullOrWhiteSpace(newCorrID))
{
_corId = newCorrID;
}
// No X-CORRELATION-ID sent in headers
if (string.IsNullOrWhiteSpace(_corId))
{
_corId = Guid.NewGuid().ToString();
}
return _corId;
}
In the above extension method, if there is no existing correlation ID in the incoming request, it will replace it with the one I’ve passed in; if not, it will generate a new GUID.
Startup.cs
In the SendAsync override, IHttpContextAccessor is not normally available in the dependency injection service collection. In the Startup.cs, make sure to enable that functionality:
// REQUIRED: Give downstream services access to the current HTTP context
services.AddHttpContextAccessor();
This will allow access the the HttpContext in any method within the application.
Next in the Startup.cs, you’ll need to register the RequestHeaderPropagationHandler. I make sure that this is registered as a transient, so it doesn’t store values from requests from other callers/users.
// REQUIRED: This is used in to propogate headers to downstream requests
services.AddTransient<RequestHeaderPropagationHandler>();
The last step is to add an http message handler to the Http Client in the Statup.cs.
services.AddHttpClient("lookups", c =>
{
c.BaseAddress = new Uri(Configuration["AppSettings:LookupsBaseURL"]!);
})
.AddHttpMessageHandler<RequestHeaderPropagationHandler>();
Summary
So now you have the code that attempts to add an X-CORRELATION-ID header and value to any outgoing HTTP request made by this application.


















