7

In Asp.Net Web Api 2 what is the difference between setting an HttpClient Accept Header using the following traditional method :

        HttpClient client = HttpClientFactory.Create(handler);

        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

and the following method :

var headers = new Dictionary<string, string>
            {
                {"Accept", "application/json"}};

headers.ForEach(h => client.DefaultRequestHeaders.Add(h.Key, h.Value));

Update 1:

Based on the answer by @DarrenMiller in the following post What is the overhead of creating a new HttpClient per call in a WebAPI client? it appears that the preferred method is using DefaultRequestHeaders property because it contains properties which are intended for multiple calls. Does this mean if I set a default header using a simple dictionary my HttpClient client will not be as efficient as the one which uses DefaultRequestHeaders? In addition I really cant understand how the values inside DefaultRequestHeaders will be reused? Lets say I create 20 HttpClient client using HttpClientFactory.Create and inside every single one of them I set DefaultRequestHeaders property [Do I really need to do it because DefaultRequestHeaders was meant to be reused?!]. Where does this reuse kick-in and does setting the DefaultRequestHeaders every time I create a HttpClient client result in some kind of performance hit?

Community
  • 1
  • 1
MHOOS
  • 5,146
  • 11
  • 39
  • 74
  • may be because you're adding what to `Accept`, and in the second case you're adding to somewhere else. – Amit Kumar Ghosh Jun 04 '15 at 16:56
  • The best thing to do is to use something like Fiddler (http://www.telerik.com/fiddler) to observe what is actually sent in the request when you do both methods when Fiddler is running. It will record the HTTP traffic and allow you to look at the differences. – gudatcomputers Jun 04 '15 at 16:58
  • @MHOOS RE your update: you are meant to reuse your `HttpClient`. Only then the default headers gets reused. – Aldracor Aug 18 '20 at 09:56

1 Answers1

1

Part one of your question: Is there any difference for adding headers?

HttpClient client = HttpClientFactory.Create(handler);

Method 1:

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

Method 2:

var headers = new Dictionary<string, string>{{"Accept", "application/json"}};
headers.ForEach(h => client.DefaultRequestHeaders.Add(h.Key, h.Value));

Method 1 gives you nice strongly typed values with the ability to add multiple accept types. Method 2 has one more "magic string" that could be a place for typos and there is no way to add multiple accept types.

Part 2 of your question: Where is the performance and reuse value?

The performance hit of using a new HttpClient for every request depends on your use case. Get a bench mark and measure to see if it matters. The performance on the developer is most likely where the gains will be. Consider that every HttpClient you use you have to remember a bunch of headers to add. If you forget to add a proper header, errors happen. So, you can use DefaultRequestHeaders to set these up in a factory.

public class ApiService
{
    public static HttpClient GetClient()
    {
        var client = new HttpClient(new Uri("https://someservice/"));
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
        //add any other setup items here.
        return client;
    }
}

Now use it:

public async Task DoStuff()
{
    using(var client = ApiService.GetClient())
    {
        //client will have the proper base uri and all the headers set.
        var data = await client.GetAsync<dynamic>("Sales");

        //client will still have the proper base uri and all the headers set.
        var data2 = await client.GetAsync<dynamic>("Products");
    }
}

HttpClients should be short lived and always wrapped in a using statement. The reuse occurs when multiple requests are made using the same client.

UPDATE:

As others have mentioned, Microsoft recommends keeping one HttpClient for the life of the application to avoid excess overhead. The preferred way to get an HttpClient would be having a static one declared and then reference that as needed. Be aware that anything set in the default headers will go out with every request so make sure you don't put things there such as Authorization unless you're sure of the final destination of the request.

ManOVision
  • 1,853
  • 1
  • 12
  • 14
  • 3
    "HttpClients should be short lived" - there is other guidance (example: http://stackoverflow.com/a/22561368/306430) that contradicts this. It can be expensive to rebuild HttpClient (and reestablish underlying connection) so one can also reuse HttpClient long-term for sites sensitive to performance and/or scalability. – codingoutloud Jan 24 '17 at 20:44
  • 1
    I guess "short lived" is subjective. There is never a catch all solution. As I mentioned above, the individual use case will determine the best solution. Running this through a for-loop, probably not a good idea. Keeping a single HttpClient alive for the entire use of a native app for hours on end, probably not a good idea. There will always be a compromise somewhere. Thanks for the feedback. – ManOVision Jan 24 '17 at 22:33
  • Never wrap your HttpClient in using statements (with some rare exceptions maybe). Always reuse HttpClient where you can to prevent socket exhaustion, see [this post](https://www.stevejgordon.co.uk/httpclient-connection-pooling-in-dotnet-core) for more info regardring connection lifetime worries. – Aldracor Aug 18 '20 at 09:52
  • @Aldracor, you're right that you shouldn't wrap the HttpClient in a using. This is now widely known, but 5 years ago when I answered this, it wasn't well known. Also, it depends on how your app is structured. If this were an Azure Function, wrapping it would be perfectly fine since that is a run once scenario. Never is a harsh word ;) – ManOVision Aug 28 '20 at 17:18
  • I have added an update to the answer to reflect that a single HttpClient for the life of the application is preferred. – ManOVision Aug 28 '20 at 17:33