Named HttpClient Registrations
Introduction
You're most likely familiar with HttpClient and its recommended usage, which involves calling one of the overloads of AddHttpClient. One thing that you may or may not have noticed is, even though you call the overload that takes a name for the client, it is not registered in the dependency injection (DI) container as such. So I went out to fix this!
So, what we want is that any named clients can be also registered with the DI container, which will allow us to retrieve them by the service type and key:
var googleClient = serviceProvider.GetRequiredKeyedService<HttpClient>("Google");
or:
public IActionResult Index([FromKeyedServices("Google")] HttpClient googleClient) { ... }
So, as of now, the only way to obtain the named client is by using IHttpClientFactory's CreateClient method:
var googleClient = serviceProvider.GetRequiredService<IHttpClientFactory>().CreateClient("Google");
Which is fine, but just takes some extra work. Let's see how we can circumvent this.
Implementation for .NET < 9
This is what I came up with, an extension method AddAsKeyed over IHttpClientBuilder:
public static class HttpClientBuilderExtensions
{
public static IHttpClientBuilder AddAsKeyed(this IHttpClientBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder, nameof(builder)); ArgumentException.ThrowIfNullOrWhiteSpace(builder.Name, nameof(builder.Name)); builder.Services.AddKeyedTransient(builder.Name, (sp, key) => sp.GetRequiredService<IHttpClientFactory>().CreateClient((string)key)); return builder;
}
}
Which we can use like this:
builder.Services.AddHttpClient("Google", static client =>
{
builder.BaseAddress = new("https://google.com");
}).AddAsKeyed();
As you can see, it's very easy to implement and use, it just leverages the IHttpClientFactory, but keep in mind that you should only call it for a named service - otherwise, it would be pointless anyway!
.NET 9
Realising this limitation, .NET 9 introduced its own AddAsKeyed method, which you will be able to use as soon as .NET 9 comes out! The ticket that requested it is https://github.com/dotnet/runtime/issues/89755. The methods do similar things, but I understand that my implementation will only be needed for versions of .NET before 9.
Conclusion
As always, hope you find this useful! Let me know what you think!
Comments
Post a Comment