56

I am trying to create layer for webservice using HttpClient in my Xamarin.Forms mobile app.

  1. without singlton pattern
  2. with singleton pattern

in first approach i am creating new http client object in each new request made by mobile applicaiton.

here is my code

  public HttpClient GetConnection()
        {

            HttpClient httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri(baseAddress); 
            httpClient.Timeout = System.TimeSpan.FromMilliseconds(timeout);


            return httpClient;

        }

post request code

 public async Task<TResult> PostAsync<TRequest, TResult>(String url, TRequest requestData)
        {
            HttpClient client = GetConnection();
            String responseData = null;
            if (client != null)
            {

                String serializedObject = await Task.Run(() => JsonConvert.SerializeObject(requestData, _jsonSerializerSettings));
                var jsonContent = new StringContent(serializedObject, System.Text.Encoding.UTF8, "application/json");
                HttpResponseMessage response = await client.PostAsync(new Uri(url, UriKind.Relative), jsonContent);
                responseData = await HandleResponse(response);


                return await Task.Run(() => JsonConvert.DeserializeObject<TResult>(responseData, _jsonSerializerSettings));


            }
            else
            {

                throw new NullReferenceException("NullReferenceException @ PostAsync  httpclient is null WebRequest.cs");

            }

        }

client will use following code to execute request

new LoginService(new WebRequest()).UserLogin(userRequest);

inside class that implements IWebRequest

_webRequest.PostAsync<UserRequest,bool>(Constants.USER_LOGIN, userRequest);

in second approach i am reusing the same http client object in each new request here , my singleton class is thread safe too.

private static readonly Lazy<HttpService> lazy =
        new Lazy<HttpService>(() => new HttpService());

        public static HttpService Instance { get { return lazy.Value; } }



        private HttpClient getConnection()
        {

            client = new HttpClient();
            client.Timeout = System.TimeSpan.FromMilliseconds(timeout);

            //client.MaxResponseContentBufferSize = 500000;
            client.BaseAddress = new Uri(baseAddress);
            return client;
        }

post request code

public Task<HttpResponseMessage> sendData(String url,String jsonData)
        {

            var jsonContent = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json");

            return getConnection().PostAsync(new Uri(url, UriKind.Relative), jsonContent);
        }

client will use following code to execute

HttpService.Instance.sendData(...)

i have gone through many libraries like RestSharp over web just to explore the best and i found that most of them are creating new objects per request. so i am confused which pattern fits best.

Hunt
  • 8,215
  • 28
  • 116
  • 256
  • 11
    Don't keep creating `HttpClient`, and [here's why](https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/). – DavidG Feb 14 '18 at 02:18
  • HttpClient implements IDisposable which must be satisfied (unless you're using a static instance of it) – taiji123 Jun 14 '19 at 22:36
  • Does this answer your question? [What is the overhead of creating a new HttpClient per call in a WebAPI client?](https://stackoverflow.com/questions/22560971/what-is-the-overhead-of-creating-a-new-httpclient-per-call-in-a-webapi-client) – Michael Freidgeim Aug 09 '21 at 02:49

5 Answers5

91

Update: It seems that using a single static instance of HttpClient doesn't respect DNS changes, so the solution is to use HttpClientFactory. See here for Microsoft docs about it.

To use the HttpClientFactory you have to use Microsoft's dependency injection. This is the default for ASP.NET Core projects, but for others you will have to reference Microsoft.Extensions.Http and Microsoft.Extensions.DependencyInjection.

Then when you're creating your service container, you simply call AddHttpClient():

var services = new ServiceCollection();
services.AddHttpClient()
var serviceProvider = services.BuildServiceProvider();

And then you can inject IHttpClientFactory into your services, and behind the scenes HttpClientFactory will maintain a pool of HttpClientHandler objects - keeping your DNS fresh and preventing problems with connection pool exhaustion.


Old answer:

Singleton is the correct way to use HttpClient. Please see this article for full details.

Microsoft docs state:

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors. Below is an example using HttpClient correctly.

And indeed, we found this in our application. We have code that can potentially make hundreds of API requests in a foreach loop, and for each iteration we were creating an HttpClient wrapped in a using. We soon started getting red herring errors from our MongoClient saying that it had timed out trying to connect to the database. After reading the linked article, we found that even after disposing of HttpClient, and realised that we were exhausting the available sockets.

The only thing to note is that things like DefaultRequestHeaders and BaseAddress will be applied anywhere that HttpClient is used. As a singleton, this is potentially throughout the application. You can still create multiple HttpClient instances in your application, but just be aware that each time you do, they create a new connection pool and, as such, should be created sparingly.

As pointed out by hvaughan3, you also can't change the instance of HttpMessageHandler used by the HttpClient, so if this matters to you, you would need to use a separate instance with that handler.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • 3
    You also cannot change out the handler, meaning if you have to handle different auth providers, you will need an `HttpClient` instance for each. – hvaughan3 Feb 14 '18 at 05:12
  • @hvaughan3 Good point. I've added it to the answer, thanks :) – ProgrammingLlama Feb 14 '18 at 05:14
  • @john I do understand the point but that leads me to another doubt that how can we still benifit with `IWebRequest` interface that i have used in my first approach to invoke `PostAsync` by making `WebRequest` class singleton – Hunt Feb 14 '18 at 06:10
  • @Hunt The way I've approached requests is to use `SendAsyc` and then build the `HttpRequestMessage` myself. Note that you don't need to make your WebRequest class a singleton, you can just make HttpClient a singleton. – ProgrammingLlama Feb 14 '18 at 06:17
  • in my question i have written the code , can you please check this is the way you are saying ? – Hunt Feb 14 '18 at 06:20
  • @Hunt The changes you need to make are: `BaseAddress` would apply to all `HttpClient` requests. If you don't want this to be the case, you would have to move it into `WebRequest` and manually combine it with the relative URL. `GetConnection()` should always return the same instance. – ProgrammingLlama Feb 14 '18 at 06:27
  • probably this will isolate the baseaddress `WebRequest.HttpInstance.BaseAddress = new Uri(GetBaseAddress());` by passing the `GetBaseAddress` function in uri – Hunt Feb 14 '18 at 06:50
  • @Hunt That will work fine if all of your requests go to the same base address. You shouldn't change BaseAddress in the middle of your application for obvious reasons (e.g. threading). – ProgrammingLlama Feb 14 '18 at 06:57
  • can you share some trick to isolate base address as some how m not able able to isolate it in `WebRequest` – Hunt Feb 14 '18 at 07:48
  • from now on, I'm just going to forward this to people when I see `new HttpClient()` :) – jpgrassi Dec 14 '18 at 13:00
  • [How to Add Generated HttpClient to ASP.NET Core Dependency Injection (Right Way)](https://medium.com/@dmitry.pavlov/how-to-add-generated-httpclient-to-asp-net-core-dependency-injection-right-way-fec21b3385f1) – Dmitry Pavlov May 27 '19 at 10:12
  • @John So what's the lifetime of the `HttpClient` created via `clientFactory.CreateClient()` ? – Legends Oct 11 '19 at 12:52
  • https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ – Golide Feb 10 '22 at 01:42
  • @goldie Is there something wrong with the link to that in my answer? – ProgrammingLlama Feb 10 '22 at 04:10
13

While HttpClient is supposed to be reused, it does not necessarily mean we have to use singleton to organize our code. Please refer to my answer here. Also quoted below.


I'm late to the party, but here is my learning journey on this tricky topic.

1. Where can we find the official advocate on reusing HttpClient?

I mean, if reusing HttpClient is intended and doing so is important, such advocate is better documented in its own API documentation, rather than being hidden in lots of "Advanced Topics", "Performance (anti)pattern" or other blog posts out there. Otherwise how is a new learner supposed to know it before it is too late?

As of now (May 2018), the first search result when googling "c# httpclient" points to this API reference page on MSDN, which does not mention that intention at all. Well, lesson 1 here for newbie is, always click the "Other Versions" link right after the MSDN help page headline, you will probably find links to the "current version" there. In this HttpClient case, it will bring you to the latest document here containing that intention description.

I suspect many developers who was new to this topic did not find the correct documentation page either, that's why this knowledge is not widely spread, and people were surprised when they found it out later, possibly in a hard way.

2. The (mis?)conception of using IDisposable

This one is slightly off-topic but still worth pointing out that, it is not a coincidence to see people in those aforementioned blog posts blaming how HttpClient 's IDisposable interface makes them tend to use the using (var client = new HttpClient()) {...} pattern and then lead to the problem.

I believe that comes down to an unspoken (mis?)conception: "an IDisposable object is expected to be short-lived".

HOWEVER, while it certainly looks like a short-lived thing when we write code in this style:

using (var foo = new SomeDisposableObject())
{
    ...
}

the official documentation on IDisposable never mentions IDisposable objects have to be short-lived. By definition, IDisposable is merely a mechanism to allow you to release unmanaged resources. Nothing more. In that sense, you are EXPECTED to eventually trigger the disposal, but it does not require you to do so in a short-lived fashion.

It is therefore your job to properly choose when to trigger the disposal, base on your real object's life cycle requirement. There is nothing stopping you from using an IDisposable in a long-lived way:

using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");

            using (var client = new HttpClient())
            {
                for (...) { ... }  // A really long loop

                // Or you may even somehow start a daemon here

            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

With this new understanding, now we revisit that blog post, we can clearly notice that the "fix" initializes HttpClient once but never dispose it, that is why we can see from its netstat output that, the connection remains at ESTABLISHED state which means it has NOT been properly closed. If it were closed, its state would be in TIME_WAIT instead. In practice, it is not a big deal to leak only one connection open after your entire program ends, and the blog poster still see a performance gain after the fix; but still, it is conceptually incorrect to blame IDisposable and choose to NOT dispose it.

3. Do we have to put HttpClient into a static property, or even put it as a singleton?

Based on the understanding of the previous section, I think the answer here becomes clear: "not necessarily". It really depends on how you organize your code, as long as you reuse an HttpClient AND (ideally) dispose it eventually.

Hilariously, not even the example in the Remarks section of the current official document does it strictly right. It defines a "GoodController" class, containing a static HttpClient property that will not be disposed; which disobeys what another example in the Examples section emphasizes: "need to call dispose ... so app doesn't leak resources".

And lastly, singleton is not without its own challenges.

"How many people think global variable is a good idea? No one.

How many people think singleton is a good idea? A few.

What gives? Singletons are just a bunch of global variables."

-- Quoted from this inspiring talk, "Global State and Singletons"

PS: SqlConnection

This one is irrelevant to the current Q&A, but it is probably a good-to-know. SqlConnection usage pattern is different. You do NOT need to reuse SqlConnection, because it will handle its connection pool better that way.

The difference is caused by their implementation approach. Each HttpClient instance uses its own connection pool (quoted from here); but SqlConnection itself is managed by a central connection pool, according to this.

And you still need to dispose SqlConnection, same as you are supposed to do for HttpClient.

RayLuo
  • 17,257
  • 6
  • 88
  • 73
  • 1
    I would recommend copy/pasting it since it is on a *different stack exchange*. If they change the url or (crazy as it sounds) decide to let that stack exchange go, the answer would be here. – Erik Philips May 07 '19 at 01:13
  • 1
    @ErikPhilips As a developer, I feel like I love DRY and hate [WET](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself#DRY_vs_WET_solutions). But since you are asking for it, here you go. – RayLuo May 07 '19 at 22:16
7

.NET Core 2.1+

When you can use DI:

    using System.Net.Http;

    public class SomeClass
    {
        private readonly IHttpClientFactory _httpClientFactory;

        public SomeClass(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public void Foo()
        {
            var httpClient = _httpClientFactory.CreateClient();
            ...
        }
    }

When you can't use DI:

    using System.Net.Http;

    public class SomeClass
    {
        private static readonly HttpClient Client;

        static SomeClass()
        {
            var handler = new SocketsHttpHandler
            {
                // Sets how long a connection can be in the pool to be considered reusable (by default - infinite)
                PooledConnectionLifetime = TimeSpan.FromMinutes(1),
            };

            Client = new HttpClient(handler, disposeHandler: false);
        }
        
        ...
    }

Reference https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests#alternatives-to-ihttpclientfactory

DonSleza4e
  • 562
  • 6
  • 11
1

As others mentioned, mostly HttpClient should be used as singleton, but there is one exception - you should not use HttpClient as singleton when you use HTTP long polling technique, because you will block other requests execution.

For long polling requests you should create separate HttpClient.

mkul
  • 773
  • 1
  • 10
  • 28
1

If you will use HttpClient as static property in WebApi applicaion, you can get following error

System.InvalidOperationException: Concurrent reads or writes are not supported.\r\n   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()\r\n   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)\r\n   at System.IO.Pipelines.Pipe.GetReadAsyncResult()\r\n   at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.WriteBody(Boolean flush)","ClassName":"IISHttpContext","MethodName":"WriteBody","EventId":{"Id":3,"Name":"UnexpectedError"},"SourceContext":"Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer"

Error will appear, when inside of you action in webapi controller you are making 2 concurrent requests to same url using HttpClient static instance

therefore i think usage of _httpClientFactory.CreateClient(Guid.NewGuid().ToString()) in action is most safe approach. According to documentation of the method - " It is generally not necessary to dispose of the System.Net.Http.HttpClient as the System.Net.Http.IHttpClientFactory tracks and disposes resources used by the System.Net.Http.HttpClient."