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
1 md service.status.middleware
2 cd service.status.middleware
3 dotnet new
4 dotnet restore
5 code .
Add the middleware ServiceStatusMiddleware class
Create a Middleware folder in your project and add a ServiceStatusMiddleware.cs file with the following contents
1namespace WebApplication
2{
3 using System;
4 using System.Net;
5 using System.Threading.Tasks;
6 using Microsoft.AspNetCore.Builder;
7 using Microsoft.AspNetCore.Http;
8 using Newtonsoft.Json;
9
10 /// <summary>
11 /// Service Status Middleware used to check the Health of your service.
12 /// </summary>
13 public class ServiceStatusMiddleware
14 {
15 /// <summary>
16 /// Next request RequestDelegate
17 /// </summary>
18 private readonly RequestDelegate next;
19
20 /// <summary>
21 /// Health check function.
22 /// </summary>
23 private readonly Func<Task<bool>> serviceStatusCheck;
24
25 /// <summary>
26 /// ServiceStatus enpoint path
27 /// </summary>
28 private static readonly PathString statePath = new PathString("/_check");
29
30 /// <summary>
31 /// Constructor
32 /// </summary>
33 ///
34 ///
35 public ServiceStatusMiddleware(RequestDelegate next, Func<Task<bool>> serviceStatusCheck)
36 {
37 this.next = next;
38 this.serviceStatusCheck = serviceStatusCheck;
39 }
40
41 /// <summary>
42 /// Where the middleware magic happens
43 /// </summary>
44 ///
45 /// <returns>Task</returns>
46 public async Task Invoke(HttpContext httpContext)
47 {
48 // If the path is different from the statePath let the request through the normal pipeline.
49 if (!httpContext.Request.Path.Equals(statePath))
50 {
51 await this.next.Invoke(httpContext);
52 }
53 else
54 {
55 // If the path is statePath call the health check function.
56 await CheckAsync(httpContext);
57 }
58 }
59
60 /// <summary>
61 /// Call the health check function and set the response.
62 /// </summary>
63 ///
64 /// <returns>Task</returns>
65 private async Task CheckAsync(HttpContext httpContext)
66 {
67 if (await this.serviceStatusCheck().ConfigureAwait(false))
68 {
69 // Service is available.
70 await WriteResponseAsync(httpContext, HttpStatusCode.OK, new ServiceStatus(true));
71 }
72 else
73 {
74 // Service is unavailable.
75 await WriteResponseAsync(httpContext, HttpStatusCode.ServiceUnavailable, new ServiceStatus(false));
76 }
77 }
78
79 /// <summary>
80 /// Writes a response of the Service Status Check.
81 /// </summary>
82 ///
83 ///
84 ///
85 /// <returns>Task</returns>
86 private Task WriteResponseAsync(HttpContext httpContext, HttpStatusCode httpStatusCode, ServiceStatus serviceStatus)
87 {
88 // Set content type.
89 httpContext.Response.Headers["Content-Type"] = new[] { "application/json" };
90
91 // Minimum set of headers to disable caching of the response.
92 httpContext.Response.Headers["Cache-Control"] = new[] { "no-cache, no-store, must-revalidate" };
93 httpContext.Response.Headers["Pragma"] = new[] { "no-cache" };
94 httpContext.Response.Headers["Expires"] = new[] { "0" };
95
96 // Set status code.
97 httpContext.Response.StatusCode = (int)httpStatusCode;
98
99 // Write the content.
100 var content = JsonConvert.SerializeObject(serviceStatus);
101 return httpContext.Response.WriteAsync(content);
102 }
103 }
104
105 /// <summary>
106 /// ServiceStatus to hold the response.
107 /// </summary>
108 public class ServiceStatus
109 {
110 public ServiceStatus(bool available)
111 {
112 Available = available;
113 }
114
115 /// <summary>
116 /// Tells if the service is available
117 /// </summary>
118 /// <returns>True if the service is available</returns>
119 public bool Available { get; }
120 }
121
122 /// <summary>
123 /// Service Status Middleware Extensions
124 /// </summary>
125 public static class ServiceStatusMiddlewareExtensions
126 {
127 public static IApplicationBuilder UseServiceStatus(
128 this IApplicationBuilder app,
129 Func<Task<bool>> serviceStatusCheck)
130 {
131 app.UseMiddleware<ServiceStatusMiddleware>(serviceStatusCheck);
132
133 return app;
134 }
135 }
136
137}
Add the middleware to the pipeline
In the Startup class find the app.AddMvc line and add the following just above
1// Call the ServiceStatus middleware with a random failing function.
2// Feel free to replace the function with any check you need.
3Random random = new Random();
4app.UseServiceStatus(() =>
5{
6 return Task.FromResult(random.Next(10) != 1);
7});
Run the application
Open a command prompt and run
1 dotnet run
Browse to: http://localhost:5000/_check and your browser should show
1{"Available":true}
or
1{"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!
Comments