diff --git a/src/Middleware/HealthChecks/src/Builder/HealthCheckEndpointRouteBuilderExtensions.cs b/src/Middleware/HealthChecks/src/Builder/HealthCheckEndpointRouteBuilderExtensions.cs new file mode 100644 index 000000000000..8ea35b8043ae --- /dev/null +++ b/src/Middleware/HealthChecks/src/Builder/HealthCheckEndpointRouteBuilderExtensions.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Routing; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Provides extension methods for to add health checks. + /// + public static class HealthCheckEndpointRouteBuilderExtensions + { + /// + /// Adds a health checks endpoint to the with the specified template. + /// + /// The to add the health checks endpoint to. + /// The URL pattern of the health checks endpoint. + /// A convention builder for the health checks endpoint. + public static IEndpointConventionBuilder MapHealthChecks( + this IEndpointRouteBuilder builder, + string pattern) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return MapHealthChecksCore(builder, pattern, null); + } + + /// + /// Adds a health checks endpoint to the with the specified template and options. + /// + /// The to add the health checks endpoint to. + /// The URL pattern of the health checks endpoint. + /// A used to configure the health checks. + /// A convention builder for the health checks endpoint. + public static IEndpointConventionBuilder MapHealthChecks( + this IEndpointRouteBuilder builder, + string pattern, + HealthCheckOptions options) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + return MapHealthChecksCore(builder, pattern, options); + } + + private static IEndpointConventionBuilder MapHealthChecksCore(IEndpointRouteBuilder builder, string pattern, HealthCheckOptions options) + { + var args = options != null ? new[] { Options.Create(options) } : Array.Empty(); + + var pipeline = builder.CreateApplicationBuilder() + .UseMiddleware(args) + .Build(); + + return builder.Map(pattern, "Health checks", pipeline); + } + } +} diff --git a/src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj b/src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj index 7a5a16a4dbf9..8ac2df90d715 100644 --- a/src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj +++ b/src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj @@ -17,6 +17,7 @@ + diff --git a/src/Middleware/HealthChecks/test/UnitTests/HealthCheckEndpointRouteBuilderExtensionsTest.cs b/src/Middleware/HealthChecks/test/UnitTests/HealthCheckEndpointRouteBuilderExtensionsTest.cs new file mode 100644 index 000000000000..07a32a574a75 --- /dev/null +++ b/src/Middleware/HealthChecks/test/UnitTests/HealthCheckEndpointRouteBuilderExtensionsTest.cs @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.TestHost; +using Moq; +using Xunit; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Diagnostics.HealthChecks +{ + public class HealthCheckEndpointRouteBuilderExtensionsTest + { + [Fact] + public async Task MapHealthChecks_ReturnsOk() + { + // Arrange + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseRouting(routes => + { + routes.MapHealthChecks("/healthz"); + }); + }) + .ConfigureServices(services => + { + services.AddRouting(); + services.AddHealthChecks(); + }); + var server = new TestServer(builder); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("/healthz"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString()); + Assert.Equal("Healthy", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task MapHealthChecks_WithOptions_ReturnsOk() + { + // Arrange + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseRouting(routes => + { + routes.MapHealthChecks("/healthz", new HealthCheckOptions + { + ResponseWriter = async (context, report) => + { + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync("Custom!"); + } + }); + }); + }) + .ConfigureServices(services => + { + services.AddRouting(); + services.AddHealthChecks(); + }); + var server = new TestServer(builder); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("/healthz"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString()); + Assert.Equal("Custom!", await response.Content.ReadAsStringAsync()); + } + } +}