I have a web application in .NET Core that leverages a third-party api and I'm wondering if a 'better' pattern would be to encapsulate all of these typed http client services into a single service that then, gets injected to each of the services, rather than injecting a new client into them. From what I've read on HttpClient it seems the optimal usage is to have a single HttpClient instance used for the whole application. All of them target the same base api but are seperated by the different endpoints/features.
I have some code in my Startup class that reads something like this
var _thirdPartyAppKey = Configuration["ThirdPartyConfig:ThirdPartyAppKey"];
services.AddHttpClient<IAuthenticationService, AuthenticationService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<ICustomerService, CustomerService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<ITransactionService, TransactionService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddHttpClient<IConsumerService, ConsumerService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
I was thinking of refactoring to something like this:
services.AddHttpClient<IThirdPartyClientService, ThirdPartyClientService>(client =>
{
client.BaseAddress = new Uri("https://api.thirdparty.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Thirdparty-App-Key", _thirdPartyAppKey);
});
services.AddScoped<IAuthenticationService, AuthenticationService>();
services.AddScoped<ICustomerService, CustomerService>();
... the rest
And then if my ThirdPartyClientService class was just this:
public class ThirdPartyClientService {
public HttpClient _httpClient;
public ThirdPartyClientService(HttpClient httpClient) {
_httpClient = httpClient;
}
}
I could inject it into my other services and just use it like:
_thirPartyClientService._httpClient.PostAsync() etc..
If all you're doing is publicly exposing the underlying HttpClient within the typed client class and calling its PostAsync() method directly and all the different versions share the exact same settings, then you're not really deriving any value from the typed client anyway; a lot of the value of that typed client is in offering explicit abstractions over the top of the HttpClient that is sitting underneath, such as having DoSomeTransactionServiceSpecificOperation() instead of exposing the HttpClient to the consumer. Furthermore, each typed client is creating a different named base handler in HttpClientFactory's handler pool, and if that base handler is essentially the same, a single handler could be reused across all of them instead.
That said, if you do start taking advantage of abstracted methods over the top of the HttpClient for each of the different interfaces, you could either:
1) Keep the separate clients if there's reasonable expectation that their incoming parameters and methods will be unique, thereby letting them continue to be single responsibility.
or
2) Keep the separate interfaces but still only have the one concrete implementation that covers all of them, i.e. public class ThirdPartyClient : IAuthenticationService, ITransactionService ... and register the single typed client to each of the interfaces at startup. This way, when the client is injected somewhere by one interface, it will only be scoped to the methods of that interface, but you can continue managing the shared code in a single implementation until it no longer makes sense to do so, and as a bonus, the underlying handlers will be shared in the pool.
Related
I have a class into which the IHttpClientFactory is injected via the constructor. There's also a HttpClient private field in this class.
Are there any issues with creating the HttpClient in the constructor, using the factory, and then reusing that HttpClient in two/multiple methods within that one class to make two/multiple different api calls? (Same Api, different endpoints)
Or would it be better to use the factory in each method to create a new client. What are the implications/pros & cons of each approach? Is any one inherently better or doesn't it matter?
private readonly HttpClient _httpClient;
public RestClient(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient();
}
public async Task<SomeResponse> Method1(SomeRequest request)
{
...
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url))
{
httpRequestMessage.Headers.Add("Accept", "application/json");
httpRequestMessage.Headers.Add("Authorization", "Basic " + credentials);
httpRequestMessage.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
using (var response = await _httpClient.SendAsync(httpRequestMessage))
{
...
}
}
...
}
public async Task<SomeOtherResponse> Method2(someInput)
{
...
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri.ToString()))
{
httpRequestMessage.Headers.Add("Accept", "image/png");
httpRequestMessage.Headers.Add("Authorization", "Basic " + credentials);
using (var response = await _httpClient.SendAsync(httpRequestMessage))
{
...
}
}
...
}
Edit: have looked at this post Should I cache and reuse HttpClient created from HttpClientFactory? but it doesn't answer my questions. If there is something to be derived from there please explain.
I think you are looking for this guidance from Microsoft: Guidelines for using HttpClient
I copy here the related part
Recommended use
In .NET Core and .NET 5+:
Use a static or singleton HttpClient instance with PooledConnectionLifetime set to the desired interval, such as two minutes, depending on expected DNS changes. This solves both the socket exhaustion and DNS changes problems without adding the overhead of IHttpClientFactory. If you need to be able to mock your handler, you can register it separately.
Using IHttpClientFactory, you can have multiple, differently configured clients for different use cases. However, be aware that the factory-created clients are intended to be short-lived, and once the client is created, the factory no longer has control over it.
The factory pools HttpMessageHandler instances, and, if its lifetime hasn't expired, a handler can be reused from the pool when the factory creates a new HttpClient instance. This reuse avoids any socket exhaustion issues.
If you desire the configurability that IHttpClientFactory provides, we recommend using the typed-client approach.
In .NET Framework:
Use IHttpClientFactory to manage your HttpClient instances. If you create a new client instance for each request, you can exhaust available sockets.
Tip
If your app requires cookies, consider disabling automatic cookie handling or avoiding IHttpClientFactory. Pooling the HttpMessageHandler instances results in sharing of CookieContainer objects. Unanticipated CookieContainer object sharing often results in incorrect code.
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);
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
I am using scoped service called IOperationContextProvider to hold some information about my current execution context (called OperationContext).
Whenever I start a new execution path (not only HTTP request, but some async impulses such as queue message, change feed change..), I create a dedicated DI service scope.
Any class can inject the provider and has access to this context (such as correlation ID).
For outgoing requests, I would like to configure to add the correlation ID to outgoing HTTP header, like this:
services.AddHttpClient<IMyClass, MyClass>((serviceProvider, httpClient) =>
{
var contextProvider = serviceProvider.GetRequiredService<IOperationContextProvider>();
var corrId = contextProvider.Context.CorrelationId;
httpClient.DefaultRequestHeaders.Add("x-corr-id", corrId);
});
However, I am unable to do this, because IHttpClientFactory creates scope for each handler it is creating and my context is not reachable from inside the HTTP client configuration. Same goes for adding HTTP message handlers, they are created in the same scope as the handler too.
Official documentation:
The IHttpClientFactory creates a separate DI scope for each handler. Handlers are free to depend upon services of any scope.
Is there any way to reach the same scope as in which the HttpClient itself is being built?
I only have found a way to where for the MyClass, where I also inject HttpClient, I inject the IOperationContextProvider too and configure manually the HttpClient but that is a bit cumbersome because it needs to be done everywhere:
public MyClass(HttpClient httpClient, IOperationContextProvider contextProvider)
{
var corrId = contextProvider.Context.CorrelationId;
httpClient.DefaultRequestHeaders.Add("x-corr-id", corrId);
this._httpClient = httpClient;
}
If you absolutely don’t want the HttpClientFactory to create a service scope, then you can disable this behavior through the HttpClientFactoryOptions.SuppressHandlerScope property. There isn’t a nice API to configure this though, so you will have to do something like this:
var httpClientBuilder = services.AddHttpClient<IMyClass, MyClass>(…);
services.Configure<HttpClientFactoryOptions>(httpClientBuilder.Name, options =>
{
options.SuppressHandlerScope = true;
});
Alternatively, you could also create the delegating handler directly, without going through DI:
services.AddHttpClient<IMyClass, MyClass>(…)
.AddHttpMessageHandler(sp =>
{
var contextProvider = sp.GetService<IOperationContextProvider>()
return new MyHandlerWithoutDI(contextProvider);
});
One of the things thats also suggested is that the shared settings like the defaultrequestheaders be properly setup to avoid race conditions, if you are planning to use this client as a shared resource. This is in reference to your initial proposed workaround.
public MyClass(HttpClient httpClient, IOperationContextProvider
contextProvider)
{
var corrId = contextProvider.Context.CorrelationId;
httpClient.DefaultRequestHeaders.Add("x-corr-id", corrId);
this._httpClient = httpClient;
}
https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/
I am using the new Web API bits in a project, and I have found that I cannot use the normal HttpMessageRequest, as I need to add client certificates to the request. As a result, I am using the HttpClient (so I can use WebRequestHandler). This all works well, except that it isn't stub/mock friendly, at least for Rhino Mocks.
I would normally create a wrapper service around HttpClient that I would use instead, but I would like to avoid this if possible, as there are a lot of methods that I would need to wrap. I am hoping that I have missing something—any suggestions on how to stub HttpClient?
As an alternative to the excellent ideas already presented by #Raj, it may be possible to go a step lower and to mock/fake the HttpMessageHandler instead.
If you make any class that needs an HttpClient accept it as a dependency injection parameter in the constructor, then when unit testing you can pass in an HttpClient that has been injected with your own HttpMessageHandler. This simple class has only one abstract method that you need to implement, as follows:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public HttpRequestMessage RequestMessage { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
RequestMessage = request;
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
}
}
My trivial example just saves the HttpRequestMessage in a public property for later inspection and returns HTTP 200 (OK), but you could augment this by adding a constructor that sets the result you want returned.
You'd use this class like this:
public void foo()
{
//Arrange
var fakeHandler = new FakeHttpMessageHandler();
var client = new HttpClient(fakeHandler);
var SUT = new ClassUnderTest(client);
//Act
SUT.DomSomething();
//Assert
fakeHandler.RequestMessage.Method.ShouldEqual(HttpMethod.Get); // etc...
}
There are limitations to this approach, for example in a method that makes multiple requests or needs to create multiple HttpClients, then the fake handler might start to become too complicated. However, it may be worth consideration for simple cases.
I released a library a few months ago called MockHttp which might be useful. It uses a custom HttpMessageHandler with a fluent (and extensible) API. You can inject the mocked handler (or HttpClient) into your service class and it will respond as it was configured.
Below shows basic usage. The When and Respond methods have a bunch of overloads, including running custom logic. The documentation on the GitHub page goes into a lot more detail.
var mockHttp = new MockHttpMessageHandler();
// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localhost/api/user/*")
.Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON
// Inject the handler or client into your application code
var client = new HttpClient(mockHttp);
var response = async client.GetAsync("http://localhost/api/user/1234");
// or without async: var response = client.GetAsync(...).Result;
var json = await response.Content.ReadAsStringAsync();
// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}
I use Moq and I can stub out the HttpClient. I think this the same for Rhino Mock (I haven’t tried by myself).
If you just want to stub the HttpClient the below code should work:
var stubHttpClient = new Mock<HttpClient>();
ValuesController controller = new ValuesController(stubHttpClient.Object);
Please correct me if I’m wrong. I guess you are referring to here is that stubbing out members within HttpClient.
Most popular isolation/mock object frameworks won’t allow you to stub/setup on non- virtual members
For example the below code throws an exception
stubHttpClient.Setup(x => x.BaseAddress).Returns(new Uri("some_uri");
You also mentioned that you would like to avoid creating a wrapper because you would wrap lot of HttpClient members. Not clear why you need to wrap lots of methods but you can easily wrap only the methods you need.
For example :
public interface IHttpClientWrapper { Uri BaseAddress { get; } }
public class HttpClientWrapper : IHttpClientWrapper
{
readonly HttpClient client;
public HttpClientWrapper() {
client = new HttpClient();
}
public Uri BaseAddress {
get
{
return client.BaseAddress;
}
}
}
The other options that I think might benefit for you (plenty of examples out there so I won’t write the code)
Microsoft Moles Framework
http://research.microsoft.com/en-us/projects/moles/
Microsoft Fakes: (if you are using VS2012 Ultimate)
http://msdn.microsoft.com/en-us/library/hh549175.aspx