HttpClient

How to use it the rigth way?

When an HTTP request is made from a browser or a client application to an API or a server, a TCP/IP socket connection is opened between the client and the server. In .NET, HttpClient is the abstraction that facilitates opening the socket at the client end and sending an HTTP request.

Behind the scenes, HttpClient leverages HttpMessageHandler (SocketsHttpHandler) to manage the socket connections from the client to the server. Unfortunately, although HttpClient implements IDisposable interface, which makes developers think to dispose of the object by wrapping it in a using statement, disposing the HttpClient does not immediately close the underlying socket connection. The connection remains in the TIME_WAIT state for a configured amount of time (value of TcpTimedWaitDelay in the client machine's registry). During this time, the connection cannot be reused, leading to new requests creating new socket connections and potentially exhausting available connections under decent load.

To work around this issue, if planning to reuse the same HttpClient object for the application's lifetime, another problem arises. When the client attempts to connect to the server, a DNS lookup occurs to translate the host name to an IP address. It's possible that the IP address mapping to the host name changes often. Therefore, reusing the same HttpClient object might result in holding onto stale data and eventual connection failure.

To address this, Microsoft recommends using IHttpClientFactory to implement resilient HTTP requests. Whenever IHttpClientFactory requests a HttpClient, a new instance is returned. However, HttpClient internally uses a pooled HttpMessageHandler, which has a lifetime. The default value is 2 minutes but can be overridden.

Below, you can see the correct way of how AddHttpClient() gets registered as transient with the DI container.

builder.Services.AddHttpClient<TodosService>(opts => {
    opts.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
});

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class TodosController : ControllerBase
{
    private readonly TodosService _todosService;

    public TodosController(TodosService todosService)
    {
        _todosService = todosService;
    }
   [HttpGet("todoscorrect")]
    public async Task<string> GetTodos() => await _todosService.GetTodos();
}
public class TodosService
{
    private readonly HttpClient _httpClient;

    public TodosService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    public async Task<string> GetTodos() {
         var a =   await _httpClient.GetStringAsync("todos").;
         return a;
    }

}