Home > OS >  How to add middleware to worker project in .net 6?
How to add middleware to worker project in .net 6?

Time:06-07

I'm wanting to add some health checks to my .net 6 worker project but I'm not sure how.

This is the sort of code I would like to add from the old style Startup.cs class

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
        {
            Predicate = _ => true,
            ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
        });
        endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
        {
            Predicate = r => r.Name.Contains("self")
        });
    });
}

In a .net 6 web application it looks like you would do something like this...

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
    {
        Predicate = _ => true,
        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
    });
    endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
    {
        Predicate = r => r.Name.Contains("self")
    });
});

app.Run();

However WebApplication.CreateBuilder(args) is not available in a worker template. Instead you get this...

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services => { services.AddHostedService<Worker>(); })
    .Build();

await host.RunAsync();

It looks like it should be possible as there is an amalgamation of approaches in the eShopOnContainers reference app here

CodePudding user response:

To set the context, if you want to expose the /hc endpoint, you need something that listens to (and replies to) HTTP requests on a /hc path. In other words, we need to add an HTTP-listener (a web host in .NET parlance) to the worker.

So, lets do that.

First, we need to configure our SDK project to use the Web SDK. Change your project type from:

<Project Sdk="Microsoft.NET.Sdk.Worker">

To:

<Project Sdk="Microsoft.NET.Sdk.Web">

Next, instead of a Host, we can use WebApplication:

var builder = WebApplication.CreateBuilder(args);

Then, we can add the two services we want (HealthCheck Worker):

builder.Services.AddHealthChecks();
builder.Services.AddHostedService<Worker>();

Then we can map the endpoints:

var app = builder.Build();
    
app.UseRouting();
app.UseEndpoints(endpoints =>
{
   endpoints.MapHealthChecks("/hc");
});

And finally run the application:

app.Run();

I found https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-6.0 a great resource to see how to convert some 3.1-style code to 6.0 style code.

CodePudding user response:

Microsoft opted to utilize a minimal hosting model, they leverage a new builder called WebApplication which you invoke via WebApplication.CreateBuilder. They have an abundance of injection points; but their new implementation for web would be:

var builder = WebApplication.CreateBuilder(args);
var application = builder.Build();

// Traditional Middleware

application.Run();

You can leverage the WebApplication.CreateBuilder approach in other non web applications without implementing their expanded IHostBuilder. What I did in my console application:

// Entry Point:
var application = WebApplication.CreateBuilder(args);
application.Host.RegisterLogger();
application.Configuration.Build("application-settings.json");
application.Services.RegisterDependencies();

using var services = application.Services.BuildServiceProvider();
var api = services.GetServicde<IApiFacadeFactory>();

// Do Stuff...

// Extension
internal static class ApplicationExtension
{
     public static void Build(this ConfigurationManager manager, string configuration) =>
          .SetBasePath(Directory.GetCurrentDirectory())
          .AddJsonFile(configuration, false, true)
          .AddUserSecrets<Program>(false, true)
          .AddEnvironmentVariables()
          .Build();

      public static void RegisterLogger(this IHostBuilder host)
      {
            host.UserSerilog((context, configuration) => configuration
                  .MinimumLevel.Information()
                  .WriteTo.Console());
  
            BuildStaticSerilog(); // Serilog required some additional logic to correctly function.
      }

      public static void RegisterDependencies(this IServiceCollection services)
      {
           services.AddLogging(logger => logger.AddSerilog());
           services.AddHttpClient<IApiFacade, ApiFacade>();

           // Additional implementations...
      }
}

So in theory, you should be able to add that directly into my extension service method to implement- My understanding is you can interchange the default builder or their new WebApplication approach.

  • Related