受欢迎的博客标签

how to use HttpClient correctly in .Net core

Published

HttpClient provides a base class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI.

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.

Here is a simple program written to demonstrate the error use of HttpClient( Do not create and destroy multiple HttpClients.):

using System;
using System.Net.Http;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Starting connections");
            for(int i = 0; i<10; i++)
            {
                using(var client = new HttpClient())
                {
                    var result = client.GetAsync("http://aspnetmonsters.com").Result;
                    Console.WriteLine(result.StatusCode);
                }
            }
            Console.WriteLine("Connections done");
        }
    }
}

This will open up 10 requests to one of the best sites on the internet http://aspnetmonsters.com and do a GET. We just print the status code so we know it is working. The output is going to be:

C:\code\socket> dotnet run
Project socket (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling socket for .NETCoreApp,Version=v1.0

Compilation succeeded.
    0 Warning(s)
    0 Error(s)

Time elapsed 00:00:01.2501667


Starting connections
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK

All work and everything is right with the world. Except that it isn't. If we pull out the netstat tool and look at the state of sockets on the machine running this we'll see:

C:\code\socket>NETSTAT.EXE
...
  Proto  Local Address          Foreign Address        State
  TCP    10.211.55.6:12050      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12051      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12053      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12054      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12055      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12056      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12057      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12058      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12059      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12060      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12061      waws-prod-bay-017:http  TIME_WAIT
  TCP    10.211.55.6:12062      waws-prod-bay-017:http  TIME_WAIT
  TCP    127.0.0.1:1695         SIMONTIMMS742B:1696    ESTABLISHED
...

 

.Net 5.x

come from:https://devblogs.microsoft.com/dotnet/net-5-new-networking-improvements/

class Program
{
    private static readonly HttpClient _client = new HttpClient()
    {
        Timeout = TimeSpan.FromSeconds(10)
    };

    static async Task Main()
    {
        var cts = new CancellationTokenSource();
        try
        {
            // Pass in the token.
            using var response = await _client.GetAsync("http://localhost:5001/sleepFor?seconds=100", cts.Token);
        }
        // If the token has been canceled, it is not a timeout.
        catch (TaskCanceledException ex) when (cts.IsCancellationRequested)
        {
            // Handle cancellation.
            Console.WriteLine("Canceled: " + ex.Message);   
        }
        catch (TaskCanceledException ex)
        {
            // Handle timeout.
            Console.WriteLine("Timed out: "+ ex.Message);
        }
    }
}

 

.Net 3.x

Inject the HttpClient into your controller.Below is an example using HttpClient correctly.

public class GoodController : ApiController
{
    private static readonly HttpClient HttpClient;

    static GoodController()
    {
        HttpClient = new HttpClient();
    }
}

Note here that we have just one instance of HttpClient shared for the entire application. Eveything still works like it use to (actually a little faster due to socket reuse). Netstat now just shows:

C:\code\socket>NETSTAT.EXE
TCP    10.211.55.6:12254      waws-prod-bay-017:http  ESTABLISHED

you need to remember these two things:

1.Make your HttpClient static.
2.Do not dispose of or wrap your HttpClient in a using unless you explicitly are looking for a particular behaviour (such as causing your services to fail).

 

this is the production-type pattern I use for managing HttpClient as a singleton:

/// <summary>
/// Inject as a singleton to avoid multiple instances of HttpClient
/// </summary>
public class ApiService : IApiService
{
    private HttpClient _httpClient;
    private TimeSpan _timeout;
    private ILogger _log;
  
    private static string _xmlMediaType = "text/xml";
    private static string _jsonMediaType = "application/json";
    private static string _formMediaType = "application/x-www-form-urlencoded";
    private static MediaTypeWithQualityHeaderValue _jsonAcceptHeader = new MediaTypeWithQualityHeaderValue(_jsonMediaType);
 
    private HttpClient Client
    {
        get
        {
            if (_httpClient == null)
            {
                var httpClientHandler = new HttpClientHandler()
                {
                    AllowAutoRedirect = true,
                    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
                };
  
                _httpClient = new HttpClient(httpClientHandler) { Timeout = _timeout };
                _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_xmlMediaType));
                _httpClient.DefaultRequestHeaders.ConnectionClose = true;
            }
 
            return _httpClient;
        }
    }
}

 

Creating HttpClient (C#) Instances With IHttpClientFactory

 

 

 

 

Microsoft documentation:

C# HttpClient Class

Make HTTP requests using IHttpClientFactory in ASP.NET Core

Use IHttpClientFactory to implement resilient HTTP requests

 

HttpClient  Networking Improvements in .NET 5

https://devblogs.microsoft.com/dotnet/net-5-new-networking-improvements/