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
Index
endpoint that will respond toGET api/healthcheck
.Index
returns a customHealthCheckReport
object which is a wrapper around the actualHealthReport
object. 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
HealthReport
object stored in thereport
variable. You would would need to change the return type toTask<HealthReport>
as well as the in theProducesResponseType
attributes.I use dependency injection to request the
HealthCheckService
andHealthCheckOptions
objects.HealthCheckService
is used to generate the actual report.HealthCheckOptions
is used so I can access the configuration we did in Setup.cs.The remaining classes are used to convert the
HealthCheck
object into the data structure I want to return from theGET api/healthchecks
endpoint.I'm only using these objects because I am not satisfied with how
HealthCheck
serializes 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