16

I'm using HttpClient to make request to WebApi.

I have written this code

 public async Task<string> ExecuteGetHttp(string url, Dictionary<string, string> headers = null)
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(url);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }
                var response = await client.GetAsync(url);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            } 
        }

Now I'm calling this method from my action.

public async Task<ActionResult> Index()
        {
            try
            {
                RestWebRequest RestWebRequest = new RestWebRequest();
                Dictionary<string, string> headers = new Dictionary<string, string>();
                headers.Add("Authorization", "bearer _AxE9GWUO__8iIGS8stK1GrXuCXuz0xJ8Ba_nR1W2AhhOWy9r98e2_YquUmjFsAv1RcI94ROKEbiEjFVGmoiqmUU7qB5_Rjw1Z3FWMtzEc8BeM60WuIuF2fx_Y2FNTE_6XRhXce75MNf4-i0HbygnClzqDdrdG_B0hK6u2H7rtpBFV0BYZIUqFuJpkg4Aus85P8_Rd2KTCC5o6mHPiGxRl_yGFFTTL4_GvSuBQH39RoMqNj94A84KlE0hm99Yk-8jY6AKdxGRoEhtW_Ddow9FKWiViSuetcegzs_YWiPMN6kBFhY401ON_M_aH067ciIu6nZ7TiIkD5GHgndMvF-dYt3nAD95uLaqX6t8MS-WS2E80h7_AuaN5JZMOEOJCUi7z3zWMD2MoSwDtiB644XdmQ5DcJUXy_lli3KKaXgArJzKj85BWTAQ8xGXz3PyVo6W8swRaY5ojfnPUmUibm4A2lkRUvu7mHLGExgZ9rOsW_BbCDJq6LlYHM1BnAQ_W6LAE5P-DxMNZj7PNmEP1LKptr2RWwYt17JPRdN27OcSvZZdam6YMlBW00Dz2T2dgWqv7LvKpVhMpOtjOSdMhDzWEcf6yqr4ldVUszCQrPfjfBBtUdN_5nqcpiWlPx3JTkx438i08Ni8ph3gDQQvl3YL5psDcdwh0-QtNjEAGvBdQCwABvkbUhnIQQo_vwA68ITg07sEYgCl7Sql5IV7bD_x-yrlHyaVNtCn9C4zVr5ALIfj0YCuCyF_l1Z1MTRE7nb");
                var getCategories = await RestWebRequest.ExecuteGetHttp("http://localhost:53646/api/Job/GetAllCategories?isIncludeChild=true", headers);
            }
            catch (HttpRequestException ex)
            {
                return View();
            }
  return View();
  }

Now It is said that HttpClient has been designed to be re-used for multiple calls.

How Can I use same httpClient object for multiple calls.

Let's suppose First I'm calling

http://localhost:53646/api/Job/GetAllCategories?isIncludeChild=true

Now In same controller I have to call another Api with diffrent header and diffrent url.

http://localhost:53646/api/Job/category/10

Should I make the global object of HttpClient and Use the same object for all API calls.

Community
  • 1
  • 1
Amit Kumar
  • 5,888
  • 11
  • 47
  • 85
  • "Should I make the global object of HttpClient and Use the same object for all API calls." I believe so. Can you think of any specific reason that you could not store it globally and reuse it for all calls? – Falgantil May 11 '16 at 09:35
  • Check out this blog post of mine (literally about HttpClient best practices in .NET Core): https://bytedev.medium.com/net-core-httpclient-best-practices-4c1b20e32c6 – bytedev Sep 23 '21 at 02:08
  • Check this article as well https://www.thecodebuzz.com/using-httpclient-best-practices-and-anti-patterns/ – NoWar Dec 10 '21 at 06:53

3 Answers3

14

The challenge in using just one HttpClient across your application is when you want to use different credentials or you try to vary the default headers for your requests (or anything in the HttpClientHandler passed in). In this case you will need a set of purpose specific HttpClients to re-use since using just one will be problematic.

I suggest creating a HttpClient per the "type" of request you wish to make and re-use those. E.g. one for each credential you need - and maybe if you have a few sets of default headers, one per each of those.

It can be a bit of a juggling act between the HttpClient properties (which are not thread safe) and need their own instance if being varied:

- BaseAddress
- DefaultRequestHeaders
- MaxResponseContentBufferSize
- Timeout

And what you can pass in to the "VERB" methods (get, put, post etc). For example, using HttpClient.PostAsync Method (String, HttpContent) you can specify your headers for the [HttpContent][3] (and not have to put them in the HttpClient DefaultHeaders).

All of the Async methods off the HttpClient are thread safe (PostAsync) etc.

Murray Foxcroft
  • 12,785
  • 7
  • 58
  • 86
  • 5
    What about creating HttpRequestMessage for each request? And then use the SendAsync method of HttpClient. That way you can specify different credentials between requests, and still re-use the same HttpClient. – Jón Trausti Arason Nov 21 '16 at 17:28
8

Just because you can, doesn't mean you should.

You don't have to, but you can reuse the HttpClient, for example when you want to issue many HTTP requests in a tight loop. This saves a tiny fraction of time it takes to instantiate the object.

Your MVC controller is instantiated for every request. So it won't harm any significant amount of time to instantiate a HttpClient at the same time. Remember you're going to issue an HTTP request with it, which will take many orders more time than the instantiation ever will.

If you do insist you want to reuse one instance, because you have benchmarked it and evaluated the instantiation of HttpClient to be your greatest bottleneck, then you can take a look at dependency injection and inject a single instance into every controller that needs it.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Good point in that you need to consider your use case. If you need the utmost maximum performance or have concerns about the number of connections open, then sharing HttpClients will benefit. When using DI, be careful not to resolve for every call or your DI overhead may negate any benefit. – Murray Foxcroft May 11 '16 at 10:40
  • @Murray HttpClient uses connection pooling. – CodeCaster May 11 '16 at 10:41
  • To clarify, every HttpClient instance uses its own connection pool. – Murray Foxcroft May 11 '16 at 11:16
  • @Murray you're right about that. However, HttpClient by default uses HttpWebRequest internally, and that in turn uses a connection pool through its ServicePoint. – CodeCaster May 11 '16 at 11:29
  • Cool - thanks for the detail of how that works down the stack. – Murray Foxcroft May 11 '16 at 12:12
  • 6
    Not only performance, but also stability and reliability of your application should be considered. https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ gives a good explanation of this. – Arve Systad Oct 11 '17 at 08:36
  • the cost of re-upping HttpClient on every request can be substantial, as much as 100ms per call depending on how you host your client.Do your due diligence and see how expensive it is to forgo a shared HttpClient instance. – Larry Smith Jun 26 '19 at 18:18
  • 1
    @Larry you're absolutely right, I've learnt about the problems with HttpClient after writing this answer. I'll update it when I can. – CodeCaster Jun 27 '19 at 07:07
5

in .net core you can do the same with HttpClientFactory something like this:

public interface IBuyService
{
    Task<Buy> GetBuyItems();
}

public class BuyService: IBuyService
{
    private readonly HttpClient _httpClient;

    public BuyService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<Buy> GetBuyItems()
    {
        var uri = "Uri";

        var responseString = await _httpClient.GetStringAsync(uri);

        var buy = JsonConvert.DeserializeObject<Buy>(responseString);
        return buy;
    }
}

ConfigureServices

services.AddHttpClient<IBuyService, BuyService>(client =>
{
     client.BaseAddress = new Uri(Configuration["BaseUrl"]);
});

documentation and example at here and here

Reza Jenabi
  • 3,884
  • 1
  • 29
  • 34