I’ve seen some fierce office arguments about how to use HttpClient in .NET since I’ve been programming. And it’s always about one thing. When exactly do you dispose of an HttpClient instance?
You see there is one train of thought that looks like this :
void DoSomething() { using(HttpClient client = new HttpClient()) { //Do stuff here } }
So you are creating a new instance every time you make an outbound call. Certainly when I first started using the HttpClient class, this seemed logical. But within the past couple of years, this particular article has become pretty infamous : https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/. With the key quotes being :
If we share a single instance of HttpClient then we can reduce the waste of sockets by reusing them
and
In the production scenario I had the number of sockets was averaging around 4000, and at peak would exceed 5000, effectively crushing the available resources on the server, which then caused services to fall over. After implementing the change, the sockets in use dropped from an average of more than 4000 to being consistently less than 400, and usually around 100.
Pretty damning stuff. So in this example, it’s instead favoured to re-use HttpClient instances across the application. And I have to admit, before this article was sent my way, I was definitely in the “always wrap it in a using statement” camp, and that’s generally all I saw out in the wild. These days it’s gone completely the other way, and you would now expect a “static” instance of HttpClient to be created and reused for the lifetime of the application. (There is actually now articles telling you to *not* use a single instance!)
But of course, in comes .NET Core with a new way to manage HttpClient lifetimes, and it’s an interesting one! This guide revolves around using .NET Core 2.1. If you aren’t using version 2.1 yet, there is a handy guide here to get up and running.
HttpClient Factories
Because we are working with .NET Core, and Core has fallen in love with “Dependency Inject all the things”! Then of course Microsoft’s solution for the HttpClient messiness is a DI solution. Let’s imagine that I’m creating an API wrapper for Twitter. So I’m going to create a “TwitterApiClient” class to encapsulate all of this work.
public interface ITwitterApiClient { Task<List<string>> GetTweets(); } public class TwitterApiClient : ITwitterApiClient { public async Task<List<string>> GetTweets() { using (HttpClient client = new HttpClient()) { //Blah blah do everything here I want to do. //var result = await client.GetAsync("/tweets"); return new List<string> { "Tweet tweet" }; } } }
For the sake of brevity, I’m not actually calling out to the Twitter API. But you get the idea that I’ve created a nice wrapper for the API, that has a method called “GetTweets” that would if I wanted to, reach out and get some tweets and return them as a list. Let’s just use our imagination here! You’ll also notice I did this the crap way where we wrap everything in a using statement. This is intentional for now, just to show how things “might have been” before we knew better!
In my ConfigureServices method I’m going to register my TwitterApiClient like so :
services.AddTransient<ITwitterApiClient, TwitterApiClient>();
Now I’m going to go ahead and create a controller that just gets these tweets and writes them on the screen :
[Route("api/[controller]")] public class TweetController : Controller { private readonly ITwitterApiClient _twitterApiClient; public TweetController(ITwitterApiClient twitterApiClient) { _twitterApiClient = twitterApiClient; } [HttpGet] public async Task<IEnumerable<string>> Get() { return await _twitterApiClient.GetTweets(); } }
Run this bad boy and what do we see?
OK so in theory we have everything working, but it’s disposing of our HttpClient each time which as we know from the above article, is a bad idea. So we could create a static instance of HttpClient, but to be perfectly honest, I hate static instances. But we are cutting edge so we are using .NET Core 2.1 (Again if you aren’t yet, you can read our guide to getting up and running with 2.1 here), and now we can use an injected instance of HttpClient. So let’s do that!
First we change our class around. We instead inject in an instance of HttpClient and use this instead.
public class TwitterApiClient : ITwitterApiClient { private readonly HttpClient _client; public TwitterApiClient(HttpClient client) { _client = client; } public async Task<List<string>> GetTweets() { //Blah blah do everything here I want to do. //var result = await _client.GetAsync("/tweets"); return new List<string> { "Tweet tweet" }; } }
Now if you run this at this point, you are gonna see an error close to :
InvalidOperationException: Unable to resolve service for type ‘System.Net.Http.HttpClient’ while attempting to activate […]
This is because Core doesn’t just inject in HttpClient’s by default, there is a tiny bit of configuration needed.
First, we need to install the Microsoft.Extensions.Http nuget package. At the time of writing this is in preview so you will need the full version install command. So from your package manager console it will be something like:
Install-Package Microsoft.Extensions.Http -Version 2.1.0-preview2-final
Now back in our ConfigureServices method in our startup.cs. We are going to add a call to AddHttpClient like so but most importantly, we remove our original call to add a transient instance of our original client. This is super important. I banged my head against a wall for a long time trying to work out what was going wrong with my code. And it turns out when you call AddHttpClient, it actually does a bunch of wiring up for you. If you then call AddTransient yourself, you just overwrite the lot!
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient<ITwitterApiClient, TwitterApiClient>(); //services.AddTransient<ITwitterApiClient, TwitterApiClient>(); services.AddMvc(); }
Give it a run and we should now be all up and running! Now what this code actually does is tell .NET Core to inject in an HttpClient instance into your nice little API wrapper, and it will handle the lifetimes for it. That last part is important. It’s not going to be a singleton, but it’s not going to be a per request type thing either. .NET Core has magic sauce under the hood that means it will at times recycle the underlying connections when it thinks it should.
I’ll admit it’s sort of hazy in a way that Microsoft says “trust us. We’ll sort this for you”. But it’s probably a whole lot better than what you were doing on your own.
Setting Defaults
Taking things a step further, we can actually set up some defaults in our configure method that mean we are configuring our application all in one place (And it’s not hardcoded in our services). As an example, I can do this :
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient<ITwitterApiClient, TwitterApiClient>(client => { client.BaseAddress = new Uri("http://api.twitter.com"); client.Timeout = TimeSpan.FromMinutes(1); }); //services.AddTransient<ITwitterApiClient, TwitterApiClient>(); services.AddMvc(); }
If we decide to read these settings from a config store like appSettings.json, we don’t have to pollute any sort of IOptions throughout our actual Twitter client. Nice!
Named HttpClient Factories
Something else you can do is create named instances of HttpClient that can be created from an HttpClientFactory. This is handy if the class you want to inject HttpClient instances into needs more than one default. So for example a “SocialMediaApiClient” that talks to both Twitter and Facebook.
The setup is slightly different. Instead of saying which class we want to inject our HttpClient into, we just add an instance of HttpClient to the factory with a particular name, and the defaults we want.
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("twitterClient", client => { client.BaseAddress = new Uri("https://api.twitter.com"); }); services.AddHttpClient("facebookClient", client => { client.BaseAddress = new Uri("https://api.facebook.com"); }); services.AddTransient<ISocialMediaApiClient, SocialMediaApiClient>(); services.AddMvc(); }
Then when it comes to our actual service we first inject an instance of IHttpClientFactory, and then we can get that specific instance of HttpClient by calling CreateClient with the client name as a parameter. Once again, the lifecycle is managed for us as to when it’s disposed of or reused.
public class SocialMediaApiClient : ISocialMediaApiClient { private readonly IHttpClientFactory _httpClientFactory; public SocialMediaApiClient(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public string[] GetTweets() { var client = _httpClientFactory.CreateClient("twitterClient"); Uri endpoint = client.BaseAddress; // Returns https://api.twitter.com ... } }
Generic HttpClient
Finally, the HttpClient factory comes with the ability to generate a new HttpClient on demand which will be managed for you. With this, there shouldn’t ever be a reason to “new up” an instance of HttpClient ever again.
First we just call “AddHttpClient” in our ConfigureServices method, passing in absolutely nothing.
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient(); .... }
And whenever we want to actually get a new instance of an HttpClient. We inject in an instance of IHttpClientFactory and call CreateClient passing in nothing extra.
public void DoSomething() { var client = _httpClientFactory.CreateClient(); }
I tried following the steps to inject HttpClient into a class in my ASP.NET Core application and got the following error:
“Unable to resolve service for type ‘System.Net.Http.HttpClient’ while attempting to activate”
Someone else also seemed to have the same problem with a typed client being injected:
https://stackoverflow.com/questions/52186856/unable-to-resolve-service-for-type-system-net-http-httpclient
I’m injecting into a class (not ViewComponent or TagHelper like in the SO question) so that solution won’t work. I was able to get it to inject the interface IHttpClientFactory and create the client so I’m able to workaround the issue. I was wondering if you knew what I was missing to get HttpClient injected in.
Also when I tried to add another interface (Ilogger) it failed to instantiate the class. Any reason why it would only allow one parementer IHttpClientFactory?
I would probably need a small repro repository on Github to really see what the issue is but at a guess it’s going to be one of two things.
1. Something within the tree is not using the servicecollection to instantiate itself. So in the stack overflow example, it seemed like ViewComponents don’t use the inbuilt DI.
2. You are using a third party DI ontop of .NET Core DI (For example using Autofac), and you are sending the service collection to be built into AutoFac before you have added the call to add your HttpClient factory.
But again, those are just guesses so any way you could upload a repo with some example code and then I can take a look 🙂
I was trying to create a small project to recreate the issue and think I’ve found the issue. I was trying to add this down the call stack and because I don’t use DI everywhere I was trying to cheat and use the activator to create the instance. Something like:
var translator = (AzureTranslator)ActivatorUtilities.CreateInstance(HttpContext.RequestServices, typeof(AzureTranslator));
When I use Constructor injection using the typed class works fine (as in your example). When I try and use ActivatorUtilities it throws the error about not being able to resolve HttpClient.
I’ve only being using the default .NET Core DI when the pages are created to pass in things like loggers, dbcontext, etc and not with my model classes and helpers. I guess I will need to revisit that as I look to use more DI in my code.
While I normally wouldn’t recommend it, if you need to use a ServiceLocator type pattern, you can use this shim here to get the ServiceCollection to do the heavy lifting for you. https://dotnetcoretutorials.com/2018/05/06/servicelocator-shim-for-net-core/
What if I don’t whant to put my http-requests logic in controller? With DI I have to pass IHttpClientFactory or IMyHttpClient from Controllers to the Model/Helpers classes. It looks not so pretty as basic examples.
Hi Kirill,
There are a couple of examples of passing it to services/helpers in the article. It’s not too brutal 🙂
– Wade
How about make your helpers live in the container as well? That’s the true power of DI.
WOW, thanks so much for the nugget about the note “most importantly, we remove our original call to add a transient instance of our original client”. I banged my head on this too. My symptom was the client that was being injected had a BaseAddress that was nulll.
Same here! This specific example really isn’t documented well within the Microsoft Docs but this is exactly what I wanted to do.
Thank you so much. This confused the hell out of me too, until I came across this post.
I have been struggling with DI in .net core until I found this article. Very helpful. Thank you