Support for NetCore 2.2 Health Checks
I have health checks set up using the new NetCore 2.2 health checks yet they do not show in Swagger UI. Can we add support for this to Swashbuckle.AspNetCore?
Swashbuckle is built on top of ApiExplorer, the API metadata component, that ships with ASP.NET Core.
If health-checks endpoints aren't surfaced by that, then they won't be surfaced by Swashbuckle. This is a fundamental aspect to the SB design and is unlikely to change anytime soon.
IMO, this sounds like a perfect candidate for a community add-on package (see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#community-packages).
If there was a willing contributor, they could start a new project called Swashbuckle.AspNetCore.HealthChecks, that exposes an extension method on SwaggerGenOptions for enabling the functionality - e.g. EnableHealthCheckDescriptions. Then behind the scenes, this could be implemented as a Document Filter (see readme) that adds the relevant Operation descriptions to the Swagger/OAI document generated by Swashbuckle.
For those interested in this, you can expose a pass-through API endpoint based on HealthCheckService which is used behind the scenes via AddHealthChecks:
[Route("api/v1/health")]
public class HealthController : Controller
{
private readonly HealthCheckService _healthCheckService;
public HealthController(HealthCheckService healthCheckService)
{
_healthCheckService = healthCheckService;
}
/// <summary>
/// Get Health
/// </summary>
/// <remarks>Provides an indication about the health of the API</remarks>
[HttpGet]
[ProducesResponseType(typeof(HealthReport), (int)HttpStatusCode.OK)]
[SwaggerOperation(OperationId = "Health_Get")]
[SwaggerResponse((int)HttpStatusCode.OK, Description = "API is healthy")]
[SwaggerResponse((int)HttpStatusCode.ServiceUnavailable, Description = "API is not healthy")]
public async Task<IActionResult> Get()
{
var report = await _healthCheckService.CheckHealthAsync();
return report.Status == HealthStatus.Healthy ? Ok(report) : StatusCode((int)HttpStatusCode.ServiceUnavailable, report);
}
}
I took a similar approach to @tomkerkhove, but since I wanted to retain the ability to customize the response using the HealthCheckOptions.ResponseWriter and use the ActionResult pattern, I created a HealthCheckActionResult
public class HealthCheckActionResult : ActionResult
{
private readonly HealthCheckOptions _healthCheckOptions;
private readonly HealthCheckService _healthCheckService;
public HealthCheckActionResult(HealthCheckService healthCheckService, HealthCheckOptions healthCheckOptions)
{
_healthCheckOptions = healthCheckOptions;
_healthCheckService = healthCheckService;
}
public HealthReport HealthReport { get; private set; }
public override async Task ExecuteResultAsync(ActionContext context)
{
HealthReport = await _healthCheckService.CheckHealthAsync(_healthCheckOptions.Predicate, context.HttpContext.RequestAborted);
context.HttpContext.Response.StatusCode = _healthCheckOptions.ResultStatusCodes[HealthReport.Status];
await _healthCheckOptions.ResponseWriter(context.HttpContext, HealthReport);
}
}
And then in my controller,
private readonly HealthCheckService _healthCheckService;
private readonly HealthCheckOptions _healthCheckOptions;
public DiagnosticsController(HealthCheckService healthCheckService, HealthCheckOptions healthCheckOptions = null)
{
_healthCheckService = healthCheckService ?? throw new ArgumentNullException(nameof(healthCheckService));
_healthCheckOptions = healthCheckOptions ?? new HealthCheckOptions();
}
[HttpGet("healthcheck")]
public HealthCheckActionResult GetHealthReport()
{
return new HealthCheckActionResult(_healthCheckService, _healthCheckOptions);
}
@tomkerkhove which version of Swashbuckle.AspNetCore are you running on? I am using your same approach, but I get a lot of errors resolving the response type.
` ``
[HttpGet]
[ProducesResponseType(typeof(HealthReport), 200)]
[Route("/health")]
public async Task
{
var report = await _sqlDataStatusService.GetDataHealth();
var healthReport = new HealthReport(report, DateTime.UtcNow);
return Ok(healthReport);
}
running locally, I see requests from the swagger like
``
Request starting HTTP/1.1 GET http://localhost:5002/swagger/doc/doc/doc/doc/doc/doc/doc/doc/doc/swagger.json
and the UI reports Resolver errorCannot read property '$ref' of undefined.
Any clue?
I'm using a variety of versions, which one are you using? Keep in mind that 5.x uses new serializer and might be due to that.
@tomkerkhove I am running on 5.0.0, so I guess the serialiser might be an issue then.
@tomkerkhove upgrading to 5.4.0 did the trick.
However the example makes no sense, even though the model is correct. Not a big deal anyway.
Thanks.
Happy to help!
@say25 (and others) can this be closed or is there still an outstanding issue?
I mean I think to some extent we can close but I do think it would be cool if these HealthChecks were exposed in ApiExplorer but I think https://github.com/dotnet/aspnetcore/issues/18153 covers it.
Most helpful comment
For those interested in this, you can expose a pass-through API endpoint based on
HealthCheckServicewhich is used behind the scenes viaAddHealthChecks: