Click here to Skip to main content
14,830,769 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi All,

I have read so many articles on HttpClient. In some articles, it is mentioned to create single instance of httpclient and reuse it in the entire application. But in some articles it is mentioned to create new instance of httpclient per webrequest.

C#
**// New instance per web request. It disposes the httpClient after use.**
using(var httpClient = new HttpClient())
{
   //  Get/ Post/ Put/ Delete
}

Could you please let me know which approach is the better?

I have created a small POC. Test 1 : Single instance of httpclient.

C#
private static readonly HttpClient _httpClient = new HttpClient();
// Note : Application Hosted in Local IIS
private const string ServerGetUrl = "http://localhost:80/api/service/getdata";
private static void TestPingWithSingleInstance()
    {

        Parallel.For(0, 5,
            i =>
            {
                    _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    var response = _httpClient.GetAsync(ServerGetUrl).GetAwaiter().GetResult();
                    var data = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    Console.WriteLine(data);
                    Console.WriteLine("==============="); 
            });
}

When I fired netstat command. It displays 5 open ports.

TCP 127.0.0.1:46336 DESKTOP-OEOGI0L:http ESTABLISHED
TCP 127.0.0.1:46337 DESKTOP-OEOGI0L:http ESTABLISHED
TCP 127.0.0.1:46338 DESKTOP-OEOGI0L:http ESTABLISHED
TCP 127.0.0.1:46339 DESKTOP-OEOGI0L:http ESTABLISHED
TCP 127.0.0.1:46340 DESKTOP-OEOGI0L:http ESTABLISHED
Test 2 : Create instance per webrequest.

C#
private static void TestPingWithDisposableInstance()
{
using (var httpClient = new HttpClient())
                {
                    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    var response = httpClient.GetAsync(ServerGetUrl).GetAwaiter().GetResult();
                    var data = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    Console.WriteLine(data);
                    Console.WriteLine("===============");
                }

}

When I fired netstat command. It displays 5 open ports.

TCP 127.0.0.1:46233 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46234 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46235 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46236 DESKTOP-OEOGI0L:http TIME_WAIT
TCP 127.0.0.1:46237 DESKTOP-OEOGI0L:http TIME_WAIT


Same numbers of ports will be open if we use single instance or instance per webrequest. Could you please let me know the better approach ?

Note : I am using .Net 4.5.1 framework.

Thanks in advance.

What I have tried:

I have created a above POC. Same number of ports open for both the approaches.
Posted
Updated 1-Jan-17 8:50am
Comments
Thomas Daniels 1-Jan-17 13:47pm
   
I can't see the comment tree on my answer anymore so I'm commenting here as reply to your last comment: I don't know the REST principles so if a POST request would not follow them, then I suppose a GET request is fine. The reason I'd recommend POST rather than GET though, is because the access token could be treated as a sort of password, and the reason to prefer POST there, is that a GET request might end up in an access log, with the password in the parameters included, and that's bad.

Both ways will work. A HttpClient can be reused.

I recommend using a single HttpClient instance. With having a single instance, you can benefit from the DefaultRequestHeaders property[^] and BaseAddress property[^]. Even more importantly, just using a single HttpClient instance will improve performance and stability of your application. This blog post describes that in detail: You're using HttpClient wrong and it is destabilizing your software | ASP.NET Monsters[^]
   
Comments
Adi_Mag 1-Jan-17 12:44pm
   
My application flow is client===>webapi===>authentication server. We get accesstoken from client request. Then we connect to authentication server for validation. For this validation, we are using httpclient. We need to set the access header of the httpclient with access token. If I use the same instance of HttpClient then the race condition comes up.

Simple POC :

Parallel.For(0, 10,
i =>
{
var guidData = Guid.NewGuid().ToString();
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.DefaultRequestHeaders.Add("Guid", guidData);
var response = _httpClient.GetAsync("http://localhost:2967/api/service/GetData").GetAwaiter().GetResul();
var data = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Console.WriteLine(data);
Console.WriteLine("===============");
});

Error : Getting error, the guid is already added.
Thomas Daniels 1-Jan-17 12:47pm
   
I don't think I get it. Your application flow seems to be a series of steps, yet you use Parallel in your code? If this Parallel execution causes a race condition then the single HttpClient is not to blame here.
Adi_Mag 1-Jan-17 12:50pm
   
I have used parallel because multiple clients try to access the application. I am mimicking the real time scenario.
Thomas Daniels 1-Jan-17 12:54pm
   
Aha, I see what's wrong. You are setting DefaultRequestHeaders every time in the loop. This is wrong: it's default for a reason. You have to set it outside of the loop. This just won't work for the "Guid" header because it's changed every time in the loop, so I would just recommend against using a header for this. If you use GET, you can pass it in the query string. If you use POST (which I would actually recommend in this case instead of GET), then pass the guid as POST data. No reason to use headers here. This will also need some change on your server-side.
Adi_Mag 1-Jan-17 12:59pm
   
Thanks a lot.
Thomas Daniels 1-Jan-17 13:01pm
   
You're welcome.
Adi_Mag 1-Jan-17 13:15pm
   
One more point :
Get Rest Endpoint : http://www.customerservices.com/Customers/{id}?access_token=12345
Is it a right approach?
My earlier approach was to send this access_token in DefaultRequestHeader. But defaultRequsetHeader Causes Issue.
Thomas Daniels 1-Jan-17 13:18pm
   
It works for GET requests. For access tokens I think I'd use POST requests though, using HttpClient.PostAsync: https://msdn.microsoft.com/en-us/library/hh138242(v=vs.118).aspx and put access_token in the POST data.
Adi_Mag 1-Jan-17 13:23pm
   
The service is secured service. To access the get_endpoint. We need to pass the access_token. We can not violate rest principles by making get request as post. Any thoughts on this ?
After lot of research and discussion. I found the best approach is to use same instance of HttpClient through out the application.

Some key stuffs you need to remember while using this approach.
a) Create HttpClient instance at application start and inject it through out the application.
b) Set default headers only once. Mostly at application start.

But in User specific scenarios. We need to set the headers according to users. So the question arises how to handle this kind of scenarios ?
Solution :

C#
Parallel.For(0, 10,
                i =>
                {
                    var guidData = Guid.NewGuid().ToString();
                    _httpClient.DefaultRequestHeaders.Clear();
                    _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));                        
var response = _httpClient.CustomGetAsync("http://localhost:2967/api/service/GetData", x => x.Headers.Add("Guid", guidData)).GetAwaiter().GetResult();
                    var data = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    Console.WriteLine(data);
                    Console.WriteLine("===============");
                });<pre lang="c#">



Create extension method to manipulate header :
C#
public static Task<HttpResponseMessage> CustomGetAsync(this HttpClient httpClient, string url, Action<HttpRequestMessage> manipulateData)
        {
            var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);

            manipulateData(httpRequestMessage);

            return httpClient.SendAsync(httpRequestMessage);
        }
   

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900