I've added Microsoft.AspNetCore.Diagnostics.HealthChecks style health checks to my application as documented by Microsoft here.
I am also using Swashbuckle to generate a swagger document. I then use NSwag to generate a client API for my other applications to use.
The problem is that the healthcheck endpoint added with MapHealthChecks in Startup.cs is not being added to the ApiExplorer. This is a problem, because it is what Swashbuckle uses to generate the swagger document.
So my question what is the best way to add the healthcheck endpoint to ApiExplorer so that Swashbuckle can include it in the swagger file?
I have attempted to manually add the health check endpoint add ApiExplorer (code below). The application ran successfully, but the swagger document did not contain the endpoint.
// from Startup.cs
public virtual void ConfigureServices(IServiceCollection services)
{
// ...
// add swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
});
// add healthchecks
services
.AddHealthChecks()
.AddDbContextCheck<DatabaseDomain.DbContext>(tags: new[] { "db" })
;
// ...
}
public virtual void Configure(IApplicationBuilder app, IHostEnvironment env, IApiDescriptionGroupCollectionProvider apiExplorer)
{
// ...
app.UseEndpoints(endpoints =>
{
endpoints.
.MapHealthChecks("/healthcheck", new HealthCheckOptions
{
Predicate = _ => true, // allow all healthchecks
AllowCachingResponses = false,
// custom writer to return health check results as JSON
ResponseWriter = (context, result) => {
context.Response.ContentType = "application/json";
// serialize the health check results
var json = System.Text.Json.JsonSerializer.Serialize(new
{
// my custom response object
});
return context.Response.WriteAsync(json);
},
})
.RequireAuthorization()
;
});
// attempt to get the healthcheck endpoint to ApiExplorer
var healthcheckDescription = new ApiDescription
{
HttpMethod = "GET",
RelativePath = "/healthcheck",
};
healthcheckDescription.SupportedRequestFormats.Add(new ApiRequestFormat
{
MediaType = "application/json"
});
healthcheckDescription.SupportedResponseTypes.Add(new ApiResponseType
{
IsDefaultResponse = true,
StatusCode = (int)HttpStatusCode.OK,
ApiResponseFormats = new List<ApiResponseFormat> {
new ApiResponseFormat
{
MediaType = "application/json"
}
}
});
apiExplorer.ApiDescriptionGroups.Items.Append(new ApiDescriptionGroup("HealthCheck", new List<ApiDescription> { healthcheckDescription }));
// configure swagger
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
// ...
}
I ended up creating a controller specifically to return healthchecks at
GET api/healthchecks.This allows me to provide information to swagger about the data types being returned by the endpoint, and control the format of the returned data.
This is the example response given by Swagger UI:
And this is an actual response:
The following is my implementation.
Startup.cs
HealthCheckController.cs
Our HealthCheckController contains a single
Indexendpoint that will respond toGET api/healthcheck.Indexreturns a customHealthCheckReportobject which is a wrapper around the actualHealthReportobject. This allows me to control the data that is returns as well as the structure. I do this because I want to add additional info such as app version and commit details.If you don't care about the format of the data that is returned, you could instead return the
HealthReportobject stored in thereportvariable. You would would need to change the return type toTask<HealthReport>as well as the in theProducesResponseTypeattributes.I use dependency injection to request the
HealthCheckServiceandHealthCheckOptionsobjects.HealthCheckServiceis used to generate the actual report.HealthCheckOptionsis used so I can access the configuration we did in Setup.cs.The remaining classes are used to convert the
HealthCheckobject into the data structure I want to return from theGET api/healthchecksendpoint.I'm only using these objects because I am not satisfied with how
HealthCheckserializes into JSON and because I want to provide additional data.For example, I add additional properties such as
ApiVersion, so I can tell what version of my application is deployed.HealthCheckReport.cs
HealthCheck.cs
HealthCheckData.cs