Dependency injection: HttpClient or HttpClientFactory? - c#

Everywhere I can see three main approaches to create clients (basic, named, typed) in DI, but I have found nowhere if to inject IHttpClientFactory or HttpClient (both possible).
Q1: What is the difference between injecting IHttpClientFactory or HttpClient please?
Q2: And if IHttpClientFactory is injected, should I use factory.CreateClient() for each call?

Summary
HttpClient can only be injected inside Typed clients
for other usages, you need IHttpClientFactory
In both scenarios, the lifetime of HttpClientMessageHandler is managed by the framework, so you are not worried about (incorrectly) disposing the HttpClients.
Examples
In order to directly inject HttpClient, you need to register a specific Typed service that will receive the client:
services.AddHttpClient<GithubClient>(c => c.BaseAddress = new System.Uri("https://api.github.com"));
Now we can inject that inside the typed GithubClient
public class GithubClient
{
public GithubClient(HttpClient client)
{
// client.BaseAddress is "https://api.github.com"
}
}
You can't inject the HttpClient inside AnotherClient, because it is not typed to AnotherClient
public class AnotherClient
{
public AnotherClient(HttpClient client)
{
// InvalidOperationException, can't resolve HttpClient
}
}
You can, however:
1. Inject the IHttpClientFactory and call CreateClient(). This client will have BaseAddress set to null.
2. Or configure AnotherClient as a different typed client with, for example, a different BaseAdress.
Update
Based on your comment, you are registering a Named client. It is still resolved from the IHttpClientFactory.CreateClient() method, but you need to pass the 'name' of the client
Registration
services.AddHttpClient("githubClient", c => c.BaseAddress = new System.Uri("https://api.github.com"));
Usage
// note that we inject IHttpClientFactory
public HomeController(IHttpClientFactory factory)
{
this.defaultClient = factory.CreateClient(); // BaseAddress: null
this.namedClient = factory.CreateClient("githubClient"); // BaseAddress: "https://api.github.com"
}

Sadly I cannot comment, but only Post an answer. Therefore I suggest you should check out the following Links:
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Regarding your Questions it more or Less boils down to this:
Q1 -> IHttpClientFactory handles the connection pools of HttpClient instances and this will help you regarding load and dispose problems as discribed in the links, if the HttpClient is used wrong.
Q2 -> yes you should use factory.create client according to microsoft docs

Related

How to inject gRPC client to the singleton service?

Grpc.Net.ClientFactory package provides gRPC integration with the IHttpClientFactory and it comes with the convinient extension method used to register gRPC clients: IServiceCollection.AddGrpcClient<TClient>().
The problem is that this method registers TClient with the transient lifetime, which is, in turn, means that it's imposible to inject it into the singleton service. And looks like there is no possibility to configure it somehow (at least the AddGrpcClient source code has nothing on that).
So the question is: how to correclty inject the gRPC client to the singleton service and benefit from the IHttpClientFactory integration at the same time? Should I inject some client factory instead?
How about using named Grpc client to register the client, using like this.
// Register on the Startup.cs
services
.AddGrpcClient<Catalog.CatalogClient>("Catalog", o =>
{
o.Address = new Uri("https://localhost:5001");
});
// Create your service
public class OrderingService : IOrderingService
{
private readonly Catalog.CatalogClient _client;
public OrderingService(GrpcClientFactory grpcClientFactory)
{
_client = grpcClientFactory.CreateClient<Catalog.CatalogClient>("Catalog");
}
}
// Register the service as singleton at Startup.cs
service.AddSingleton<IOrderingService, OrderingService>();
// now _client instance is persist as long as you doesn't do anything weird with it
The grpc client might be a new instance each time (transient), but the underlying HttpClientMessageHandler "is managed".
I see no problems injecting a transient instance into a singleton service.

In C#, How can I reuse an HttpClient for multiple services?

I've created a C# microservice which offers several different (but related) functions.
I am now creating a C# Nuget package for a client which will help other C# microservices to leverage this microservice. This will include a Service Collection Extension class to facilitate adding the client to those microservices.
In the interest of separating concerns, within the client, I've separated the functionality into three classes:
SalesforceCacheQuerier
SalesforceCacheSyncDataManipulator
SalesforceCacheAsyncDataManipulator
Each of these need to call out to the same server.
As a niave first implementation, I've composed this method:
public static IServiceCollection AddSalesforceClients(this IServiceCollection services)
{
services.AddTransient<SalesforceCacheAuthenticationHandler>();
ConfigureClient(services.AddHttpClient<ISalesforceCacheQuerier, SalesforceCacheQuerier>());
ConfigureClient(services.AddHttpClient<ISalesforceCacheSyncDataManipulator, SalesforceCacheSyncDataManipulator>());
ConfigureClient(services.AddHttpClient<ISalesforceCacheAsyncDataManipulator, SalesforceCacheAsyncDataManipulator>());
return services;
}
private static IHttpClientBuilder? ConfigureClient(IHttpClientBuilder? clientBuilder)
=> clientBuilder.ConfigureHttpClient(ConfigureClient)
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler())
.AddHttpMessageHandler<SalesforceCacheAuthenticationHandler>();
private static void ConfigureClient(IServiceProvider provider, HttpClient client)
{
SalesforceCacheSettings? settings = provider.GetRequiredService<SalesforceCacheSettings>();
client.BaseAddress = new Uri(settings.BaseUrl, settings.ApiEndpoint);
client.DefaultRequestHeaders.ExpectContinue = true;
}
However, this generates three separate HttpClients and triples the traffic for the "Identity Server" used to provide Jwt tokens.
How can I refactor this to create and reuse only a single HttpClient?
You can use HttpClientFactory and inject it in ConfigureServices
something like services.AddHttpClient();
Later wherever you need client object just give IHttpClientFactory httpClientFactory in the constructor and you can have access to client object by just asking the factory to create a client.
HttpClient = HttpClientFactory.CreateClient(); in this fashion.
In case if you want to hold the authentication for the clients and reuse it. I would suggest to maintain a dictionary for each request type.
var HttpClients = new Dictionary<string, HttpClient>();
HttpClients.Add(SalesforceCacheQuerierKey, SalesforceCacheQuerierClient);
//assuming all the auth related headers are added to this client object //SalesforceCacheQuerierClient
and pass these from startup level.
What I needed was something like this:
services.AddHttpClient(SALESFORCE_CACHE_CLIENT, (provider, client) =>
{
SalesforceCacheClientSettings? settings = provider.GetRequiredService<SalesforceCacheClientSettings>();
client.BaseAddress = new Uri(settings.BaseUrl, settings.ApiEndpoint);
client.DefaultRequestHeaders.ExpectContinue = true;
})
.AddHttpMessageHandler<SalesforceCacheAuthenticationHandler>();
services.AddHttpClient<ISalesforceCacheQuerier, SalesforceCacheQuerier>(SALESFORCE_CACHE_CLIENT);
services.AddHttpClient<ISalesforceCacheSyncDataManipulator, SalesforceCacheSyncDataManipulator>(SALESFORCE_CACHE_CLIENT);
services.AddHttpClient<ISalesforceCacheAsyncDataManipulator, SalesforceCacheAsyncDataManipulator>(SALESFORCE_CACHE_CLIENT);

Injected HttpClient ignores IHttpClientFactory configuration

I've created a custom library which automatically sets up Polly policies for specific services which depend on HttpClient.
This is done using the IServiceCollection extension methods and the typed client approach. A simplified example:
public static IHttpClientBuilder SetUpFooServiceHttpClient(this IServiceCollection services)
{
return services
.AddHttpClient<FooService>()
.AddPolicyHandler(GetRetryPolicy());
}
The example service:
public class FooService
{
private readonly HttpClient _client;
// OPTION 1
public FooService(HttpClient httpClient)
{
_client = httpClient;
}
// OPTION 2
public FooService(IHttpClientFactory httpClientFactory)
{
_client = httpClientFactory.CreateClient(GetType().Name);
}
public void DoJob()
{
var test = _client.GetAsync("http://example.com");
}
}
Fetching the service from the DI container (this is from a test project):
var services = new ServiceCollection();
services.SetUpFooServiceHttpClient();
services.AddSingleton<FooService>();
var fooService = services
.BuildServiceProvider()
.GetRequiredService<FooService>();
// Perform test
fooService.DoJob();
Note: In this test project, I also add an extra mocked handler since I'm trying to mock http status responses, but the mocked handler being present or not is identical to the Polly policy being present or not, so I omitted the mocked handler from the example code.
Notice the two different constructors in FooService. Depending on which one I comment out and which one I leave in, I get different outcomes. All other code remains untouched.
Option 1, injecting the HttpClient directly, ignores all my configuration. I get a standard http client with no Polly policy handler.
Options 2, injecting the IHttpClientFactory and requesting the client using the current type name (i.e. FooService) respect my configuration. I get a custom http client which contains the Polly policy handler (and any other handlers I may have configured, e.g. mocked handlers in my test suite)
The absence/existence of the policy handler is confirmed in both cases using debug inspection.
According to all documentation I've found on the subject, both options should be equivalent, at least in regards to the constructed HttpClient that I obtain in the end. But that is not the case here.
The documentation I find specifies that HttpClient can be injected when using typed clients:
The MSDN documentation, specifically the "typed clients" section example.
This SO answer
This blog post
I'm using a typed client but injecting a HttpClient clearly doesn't work for me.
Why does injecting a HttpClient vs injecting IHttpClientFactory work differently in my case?
In effect, you have the following two registrations for your FooService class:
services.AddHttpClient<FooService>()
services.AddSingleton<FooService>();
Because of how the DI container works behind-the-scenes, the second registration overwrites the first. If you remove the second registration, the first will be used, and so your constructor with the HttpClient parameter will be invoked.

Long lived HttpClient created by HttpClientFactory

I've read that HttpMessageHandlers are recycled every 2 minutes, but I'm not sure if a new one is assigned to an existing HttpClient?
I've tested it out by using SetHandlerLifetime(TimeSpan.FromSeconds(5)); and even after 2 minutes with countless requests, the httpClient is continuing to work, which is a good sign?
Does that mean that I have no cause for concern with DNS changes/socket exhaustion?
Inside ConfigureServices method:
var myOptions = app.ApplicationServices.GetService<IOptionsMonitor<MyOptions>>();
HttpClient httpClient = app.ApplicationServices.GetService<IHttpClientFactory>().CreateClient();
MyStaticObject.Configure(myOptions, httpClient);
EDIT: Added some sample code.
There are a few things to look at here, with the first being does MyStaticObject actually need to be static? If it does, I would recommend instead registering it as a Singleton so that you can still leverage dependency injection. Once you have done that, you can register IHttpClientFactory and use it from your code. Your ConfigureServices method may end up looking something like this
public void ConfigureServices(IServiceCollection services)
{
//The base extension method registers IHttpClientFactory
services.AddHttpClient();
services.AddSingleton<IMySingletonObject, MySingletonObject>();
}
Then in your consuming class, MySingletonObject in this case, you would configure it as such
public class MySingletonObject
{
private readonly IHttpClientFactory _clientFactory;
public MySingletonObject(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task SomeMethodThatUsesTheClient()
{
var client = _clientFactory.CreateClient();
//use the client
}
}
The reason for this is that IHttpClientFactory handles the lifetime and pool concerns for us. Per the docs:
Manages the pooling and lifetime of underlying HttpClientMessageHandler instances. Automatic management avoids common DNS (Domain Name System) problems that occur when manually managing HttpClient lifetimes.
This happens when you make the CreateClient call, so you want to do this inside the code using the client, as opposed to on startup of your application.
As a side note, if you do not need this class to be a singleton at all, you can use the extenion services.AddHttpClient<IMyClass, MyClass>() and inject an HttpClient directly into the class. The DI container will handle getting a client from the factory behind the scenes for you.
public static IServiceCollection AddApiClient(this IServiceCollection services,
Action<RgCommunicationClientOptions> action)
{
services.AddHttpClient<ISomeApiClient, SomeApiClient>()
.AddPolicyHandler(GetRetryPolicy())
.SetHandlerLifetime(TimeSpan.FromMinutes(4));
services.AddOptions();
services.Configure(action);
return services;
}
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
//.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
services.AddApiClient(options => options.BaseAddress = configuration.GetSection("ExternalServices:SomeApi")["Url"]);
You can inject HttpClient like above in the class via dependency injection. I have used Poly extension to add RetryPolicy. Also you can set the LifetTime of the handler by using SetHaandlerLifeTime. This way you can individually configure client for each client. In the logs you can see that httpHandler expires after 4 mins for the respective call. I have used the extension method to pass the options in the method, getting values via app settings.
Hope this helps.
Blockquote

What is the best approach to define an HttpClient instance in a base class of a class library project?

There is an application which has 3 interfaces and whoever wants to use this app needs to implement these interfaces. I have created a class library project which has these interface implementations that I have inherited all from the same base class to be able to have a single HttpClient. Here is what I have done so far:
public class BaseProxy
{
protected static readonly HttpClient Client;
static BaseProxy()
{
Client = new HttpClient();
}
}
and I have used this Client in all derived classes to make GetAsync and PostAsync requests as follows:
public class XProxyImplementation
{
var response = Client.GetAsync(BaseUrl + "XXXApi/GetClientSettings/").Result;
response.EnsureSuccessStatusCode();
}
None of the methods in Web API are async by the way and I chose singleton solution because I don't want to use using block for each request. My question is should I go for a DI solution or is this code enough for an app which will be used internally? All suggestions for improvement are welcome.
I have read many answers regarding to using DI containers but this is just a class library with proxy implementations.
My other concern is even if I want to use DI, currently I am not able to introduce DI in my constructor classes because the other application that uses my implementations is looking for an empty constructor. When I try to pass HttpClient parameter to the constructor I get the following error:
The current type, System.Net.Http.HttpMessageHandler, is an abstract
class and cannot be constructed
The application which uses my dlls doesn't allow me to pass any parameters to constructor that uses any abstract classes. I guess this application uses Unity to make the handshake and in some way it looks for an empty constructor. Once I try to do the following changes I am getting the error:
public BaseProxy() : this(Service.HttpClient)
{
}
public XProxyImplementation(HttpClient client) : base(client)
{
}
That's why I actually prefered singleton instance to DI implementation.
DI? Yes
DI will enable testability of your proxy classes, whereas your current implementation cannot be unit-tested. It will also improve separation of concerns: remove the responsibility of controlling HttpClient lifetime from the proxy.
Typically, you would do something like this:
public abstract class BaseProxy
{
protected readonly HttpClient Client;
protected BaseProxy(HttpClient client)
{
Client = client;
}
// ... other members
}
public class XProxyImplementation : BaseProxy
{
public XProxyImplementation(HttpClient client) : base(client)
{
}
// ... other members
public Task SendRequest() // for example
{
return Client.GetAsync("....");
}
}
During the tests, you would initialize a different instance of HttpClient, injecting a test-friendly implementation of HttpMessageHandler:
// you implement TestHttpMessageHandler that aids your tests
var httpClient = new HttpClient(new TestHttpMessageHandler());
var proxyUnderTest = new XProxyImplementation(httpClient);
See this blog post for explanation of unit testing with HttpClient and HttpMessageHandler.
DI container? No
Now that we introduced dependency injection into your code, next question is, what injection mechanism should be used.
In your specific case, I would vote against coupling to any specific DI container, because you want your library to be consumed by many different applications, and you don't want to bloat their dependencies (an application might already be using a different DI container).
Moreover, since the code you posted is very simple, a full-blown DI container would be an overkill. In production code, you can just move your singleton HttpClient to a "service locator":
public static class SingletonServices
{
public static readonly HttpClient HttpClient;
static SingletonServices()
{
HttpClient = new HttpClient();
}
}
So that when you instantiate a proxy in production code, you do this:
var proxy = new XProxyImplementation(SingletonServices.HttpClient);
I would definitely go with a DI solution for this using the Microsoft.Extensions.DependencyInjection package.
https://dzone.com/articles/dependency-injection-in-net-core-console-applicati
And you should also be very aware how you use your async methods like GetAsync.
Using .Result almost never gives the desired result and you would be better off making the method async and using an await keyword like so:
var response = await Client.GetAsync(BaseUrl + "XXXApi/GetClientSettings/");
https://montemagno.com/c-sharp-developers-stop-calling-dot-result/
is a good resource for the whys and hows of this best practice
DI is the answer. If you do not want to use ID there is an HttpClientFactory that you can implement.
You can read more here
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

Categories