Norcino
Norcino

Reputation: 6587

ASP.NET Core Health checks in multi tenant environment using routing configuration

I started using Health checks from Asp.net Core, I love them but I could not find an easy way to wire it up with tenant based routing, for example supporting:

If I succeeded with this approach I could have used tags to filter which health check to use, but unfortunately I failed to configure the routing for the request.

app.UseEndpointRouting();
app.UseHealthChecks("/{tenant}/health", new HealthCheckOptions
    {
        ResponseWriter = WriteCustomHealthResponse,
        AllowCachingResponses = false,
        Predicate = _ => _.Tags.Contains("tenant-specific")
    });

The above code is not routing correctly. I explored the possibility to use something like the below:

app.MapWhen(context => 
    context.Request.Method == HttpMethod.Get.Method &&
    context.Request.?ROUTEDATA?.SOMECHECK("/{tenant}/HealthCheck"),
                builder => builder.UseHealthChecks());

But in this case I don't have a way to check if the routing is correct.

Upvotes: 3

Views: 1969

Answers (1)

Norcino
Norcino

Reputation: 6587

So far the solution I found is to use query string parameters and use the IHttpContextAccessor to search for the tenant parameter. For this purpose I created a base abstract implementation of IHealthCheck.

public abstract class BaseTenantHealthCheck : IHealthCheck
{
    private IHttpContextAccessor _httpContextAccessor;

    public BaseTenantHealthCheck(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected string GetTenant()
    {
        return _httpContextAccessor?.HttpContext?.Request?.Query["tenant"].ToString();
    }

    protected bool IsTenantSpecificCheck() => !string.IsNullOrEmpty(GetTenant());

    public abstract Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken));
}

Within the implementation class then I pass the context accessor shown below.

public class MylAvailabilityHealthCheck : BaseTenantHealthCheck
{
    public MyAvailabilityHealthCheck(IOptionsMonitor<MyAvailabilityCheckOptions> options, IHttpContextAccessor httpContextAccessor)
    : base(httpContextAccessor)
    {
        [..]

To access the health check I use:

  • http://{url}/health [for multi tenant checks]
  • http://{url}/health?tenant=TenantName [for tenant specific checks]

I look forward to hear if there is a more elegant way to do this.

Upvotes: 1

Related Questions