Phillip Steele
Phillip Steele

Reputation: 321

What is the best practice to configure httpClients in ASP Net Core 3.0 during startup?

ASP NET Core 2.1 gave us HttpClientFactory which is a great way to build custom httpClients.

We used this method together with an endpoint configuration service that was injected into the constructor of the startup.cs class.

The code looked somewhat like:

        public Startup(IConfiguration configuration, IEndpointService endpointService)
        {
            Configuration = configuration;
            _endpointService = endpointService;
        }

        public void ConfigureServices(IServiceCollection services)
        {
           foreach(var endpoint in _endpointService)
           {
              services.AddHttpClient(endpoint.name, c => {
                 c.BaseAddress = endpoint.Address;
                 // plus other config as required
              });
           }
        }

IEndpointService was configured prior to Startup being injected with the following call.

    IWebHostBuilder webHostBuilder =
        WebHost.CreateDefaultBuilder(args)
            .ConfigureServices((webHostBuilderContext, services) => {
                services.AddSingleton<IEndpointService, EndpointService>();
            })
            .UseStartup<Startup>();

This allowed us to read information from an external data source and configure multiple named httpClients with differing configuration.

This feature has been removed in ASP NET Core 3.0.

See: https://weblogs.thinktecture.com/pawel/2017/08/aspnet-core-beware-singleton-may-not-be-singleton.html and https://andrewlock.net/avoiding-startup-service-injection-in-asp-net-core-3/

This leaves us with a question how best to achieve the same functionality in 3.0.

This is the best I've come up with so far but I'm keen to know what the guidance is on this issue.

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IEndpointService, EndpointService>();

            using (var serviceProvider = services.BuildServiceProvider())
            {
                var endpointService = serviceProvider.GetService<IEndpointService>();

                foreach (var endpointConfiguration in endpointService.GetEndpointConfiguration())
                {
                    // Create a named client and any other configuration that we need to do here.
                    services.AddHttpClient(endpointConfiguration.Name, client =>
                    {
                        client.BaseAddress = endpointConfiguration.BaseAddress;

                        // Add any extra config that is required here
                    });
                }
            }
        }

In this example I force creation of the EndpointService using serviceProvider.GetService.

I'm conscious of the fact that I've probably recreated the multiple singleton bug (see links above), but am unsure of the best approach here.

Thoughts ?

Upvotes: 1

Views: 2970

Answers (1)

Nkosi
Nkosi

Reputation: 247203

If EndpointService has no dependencies, then just create an instance while building the host and use it in the ConfigureServices convenience method on the host builder

IEndpointService endpointService = new EndpointService();
IWebHostBuilder webHostBuilder =
    WebHost.CreateDefaultBuilder(args)
        .ConfigureServices((webHostBuilderContext, services) => {
            foreach(var endpoint in endpointService.GetEndpointConfiguration()) {
                services.AddHttpClient(endpoint.name, c => {
                    c.BaseAddress = endpoint.Address;
                    // plus other config as required
                });
            }
        })
        .UseStartup<Startup>();//<--if this is still actually needed.

Upvotes: 1

Related Questions