Skip to content

Instantly share code, notes, and snippets.

@alastairtree
Created December 30, 2020 08:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alastairtree/05c2ba97f95ab0ec19c1d4221ec63bb4 to your computer and use it in GitHub Desktop.
Save alastairtree/05c2ba97f95ab0ec19c1d4221ec63bb4 to your computer and use it in GitHub Desktop.
Using stevejgordon/CorrelationId inside Azure Functions
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CorrelationId;
using CorrelationId.Abstractions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace ExampleFunc
{
public class HttpCorrelation
{
private readonly CorrelationIdOptions options;
private readonly ICorrelationContextAccessor correlationContextAccessor;
private readonly ICorrelationIdProvider correlationIdProvider;
private readonly ILogger logger;
public HttpCorrelation(ICorrelationContextAccessor correlationContextAccessor,
IOptions<CorrelationIdOptions> options, ICorrelationIdProvider correlationIdProvider = null,
ILogger logger = null)
{
this.correlationContextAccessor = correlationContextAccessor;
this.options = options.Value;
this.correlationIdProvider = correlationIdProvider;
this.logger = logger;
}
// Inspired by and thanks to https://github.com/stevejgordon/CorrelationId/blob/master/src/CorrelationId/CorrelationIdMiddleware.cs
public async Task<IActionResult> Execute(HttpRequest request, Func<Task<IActionResult>> function)
{
if (correlationIdProvider is null)
{
throw new InvalidOperationException(
"No 'ICorrelationIdProvider' has been registered. You must either add the correlation ID services" +
" using the 'AddDefaultCorrelationId' extension method or you must register a suitable provider using the" +
" 'ICorrelationIdBuilder'.");
}
var hasCorrelationIdHeader = request.Headers.TryGetValue(options.RequestHeader, out var cid) &&
!StringValues.IsNullOrEmpty(cid);
if (!hasCorrelationIdHeader && options.EnforceHeader)
{
return new BadRequestObjectResult(
$"The '{options.RequestHeader}' request header is required, but was not found.");
}
var correlationId = hasCorrelationIdHeader ? cid.FirstOrDefault() : null;
if (options.IgnoreRequestHeader || RequiresGenerationOfCorrelationId(hasCorrelationIdHeader, cid))
{
correlationId = GenerateCorrelationId(request.HttpContext);
}
if (!string.IsNullOrEmpty(correlationId) && options.UpdateTraceIdentifier)
{
request.HttpContext.TraceIdentifier = correlationId;
}
correlationContextAccessor.CorrelationContext =
new CorrelationContext(correlationId, options.RequestHeader);
if (options.IncludeInResponse && !string.IsNullOrEmpty(correlationId))
{
// apply the correlation ID to the response header for client side tracking
request.HttpContext.Response.OnStarting(() =>
{
if (!request.HttpContext.Response.Headers.ContainsKey(options.ResponseHeader))
{
request.HttpContext.Response.Headers.Add(options.ResponseHeader, correlationId);
}
return Task.CompletedTask;
});
}
try
{
IActionResult result = null;
if (options.AddToLoggingScope && !string.IsNullOrEmpty(options.LoggingScopeKey) &&
!string.IsNullOrEmpty(correlationId))
{
using (logger.BeginScope(new Dictionary<string, object>
{
[options.LoggingScopeKey] = correlationId
}))
{
await function();
}
}
else
{
result = await function();
}
return result;
}
finally
{
correlationContextAccessor.CorrelationContext = null;
}
}
private string GenerateCorrelationId(HttpContext ctx)
{
string correlationId;
if (options.CorrelationIdGenerator is object)
{
correlationId = options.CorrelationIdGenerator();
return correlationId;
}
correlationId = correlationIdProvider.GenerateCorrelationId(ctx);
return correlationId;
}
private static bool RequiresGenerationOfCorrelationId(bool idInHeader, StringValues idFromHeader) =>
!idInHeader || StringValues.IsNullOrEmpty(idFromHeader);
}
}
@alastairtree
Copy link
Author

alastairtree commented Dec 30, 2020

Example use:

public MyFunc(HttpCorrelation http)
{
      this.http = http;
}

[FunctionName(nameof("MyFunc"))]
public async Task<IActionResult> Put([HttpTrigger(AuthorizationLevel.Function, "put", Route = "endpoint")]
            HttpRequest req)
{
    return await http.ExecuteFunction(req, async () =>
    {
          // Do stuff ...
         return new AcceptedResult();
    });
}

@dsoltesz
Copy link

how are you registering services/middleware now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment