.NET Core Health Endpoint Monitoring Middleware
Categories:
3 minute read
Today I’ll show a simple example of how to create a .Net Core Health Endpoint Monitoring Middleware.
If you need to know about the Health Endpoint Monitoring Pattern check: https://msdn.microsoft.com/en-us/library/dn589789.aspx
Create the application
Open a command prompt and run
md service.status.middleware
cd service.status.middleware
dotnet new
dotnet restore
code .
Add the middleware ServiceStatusMiddleware class
Create a Middleware folder in your project and add a ServiceStatusMiddleware.cs file with the following contents
namespace WebApplication
{
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
/// <summary>
/// Service Status Middleware used to check the Health of your service.
/// </summary>
public class ServiceStatusMiddleware
{
/// <summary>
/// Next request RequestDelegate
/// </summary>
private readonly RequestDelegate next;
/// <summary>
/// Health check function.
/// </summary>
private readonly Func<Task<bool>> serviceStatusCheck;
/// <summary>
/// ServiceStatus enpoint path
/// </summary>
private static readonly PathString statePath = new PathString("/_check");
/// <summary>
/// Constructor
/// </summary>
///
///
public ServiceStatusMiddleware(RequestDelegate next, Func<Task<bool>> serviceStatusCheck)
{
this.next = next;
this.serviceStatusCheck = serviceStatusCheck;
}
/// <summary>
/// Where the middleware magic happens
/// </summary>
///
/// <returns>Task</returns>
public async Task Invoke(HttpContext httpContext)
{
// If the path is different from the statePath let the request through the normal pipeline.
if (!httpContext.Request.Path.Equals(statePath))
{
await this.next.Invoke(httpContext);
}
else
{
// If the path is statePath call the health check function.
await CheckAsync(httpContext);
}
}
/// <summary>
/// Call the health check function and set the response.
/// </summary>
///
/// <returns>Task</returns>
private async Task CheckAsync(HttpContext httpContext)
{
if (await this.serviceStatusCheck().ConfigureAwait(false))
{
// Service is available.
await WriteResponseAsync(httpContext, HttpStatusCode.OK, new ServiceStatus(true));
}
else
{
// Service is unavailable.
await WriteResponseAsync(httpContext, HttpStatusCode.ServiceUnavailable, new ServiceStatus(false));
}
}
/// <summary>
/// Writes a response of the Service Status Check.
/// </summary>
///
///
///
/// <returns>Task</returns>
private Task WriteResponseAsync(HttpContext httpContext, HttpStatusCode httpStatusCode, ServiceStatus serviceStatus)
{
// Set content type.
httpContext.Response.Headers["Content-Type"] = new[] { "application/json" };
// Minimum set of headers to disable caching of the response.
httpContext.Response.Headers["Cache-Control"] = new[] { "no-cache, no-store, must-revalidate" };
httpContext.Response.Headers["Pragma"] = new[] { "no-cache" };
httpContext.Response.Headers["Expires"] = new[] { "0" };
// Set status code.
httpContext.Response.StatusCode = (int)httpStatusCode;
// Write the content.
var content = JsonConvert.SerializeObject(serviceStatus);
return httpContext.Response.WriteAsync(content);
}
}
/// <summary>
/// ServiceStatus to hold the response.
/// </summary>
public class ServiceStatus
{
public ServiceStatus(bool available)
{
Available = available;
}
/// <summary>
/// Tells if the service is available
/// </summary>
/// <returns>True if the service is available</returns>
public bool Available { get; }
}
/// <summary>
/// Service Status Middleware Extensions
/// </summary>
public static class ServiceStatusMiddlewareExtensions
{
public static IApplicationBuilder UseServiceStatus(
this IApplicationBuilder app,
Func<Task<bool>> serviceStatusCheck)
{
app.UseMiddleware<ServiceStatusMiddleware>(serviceStatusCheck);
return app;
}
}
}
Add the middleware to the pipeline
In the Startup class find the app.AddMvc line and add the following just above
// Call the ServiceStatus middleware with a random failing function.
// Feel free to replace the function with any check you need.
Random random = new Random();
app.UseServiceStatus(() =>
{
return Task.FromResult(random.Next(10) != 1);
});
Run the application
Open a command prompt and run
dotnet run
Browse to: http://localhost:5000/_check and your browser should show
{"Available":true}
or
{"Available":false}
depending on the result of the random function we added to the middleware.
Checkout AspNetCore.Health for nice and extensible implementation of the pattern.
Also find a copy of the code here: https://github.com/cmendible/dotnetcore.samples/tree/main/service.status.middleware
Hope it helps!