Você está utilizando HttpClient da maneira correta?

Alguns problemas não são evidentes

Construir um software que funcione isolado sem realizar integrações com outros sistemas é um fato muito raro. Praticamente todo software realiza integrações com outros componentes, sejam eles internos ou externos. Atualmente, a forma mais popular para promover a integração entre sistemas é o protocolo HTTP, e para que softwares em .NET possam se comunicar através deste protocolo, conhecer a classe HttpClient é obrigatório para o desenvolvimento adequado.

O uso indevido do HttpClient pode gerar exaustão de recursos (principalmente de sockets), pois mesmo que a classe implemente a interface IDisposable, no caso específico do HttpClient, a liberação de recursos pelo método Dispose não acontece para os sockets de conexão TCP. Tal característica é bem documentada pela Microsoft, contudo muitos desenvolvedores acabam realizando implementações como o exemplo abaixo:

public async Task<IEnumerable<SpaceflightNewDTO>> GetUsingAntiPattern()
{
    using var httpClient = new HttpClient();
    var response = await httpClient.GetAsync("https://api.spaceflightnewsapi.net/v3/articles");
    var json = await response.Content.ReadAsStringAsync();
    return JsonConvert.DeserializeObject<IEnumerable<SpaceflightNewDTO>>(json);
}

Embora a utilização do using seja reconhecidamente uma boa prática em casos de implementação de IDisposable, o HttpClient foi planejado para ser utilizado de forma compartilhada, aproveitando ao máximo as conexões já criadas (sockets), mantendo-as em aberto mesmo que o objeto HttpClient seja liberado pela instrução using, sendo um potencial motivo da máquina sofrer com Socket Starvation.

Para mitigar este problema, podemos declarar uma instância de HttpClient como static, ou configurar o contêiner de injeção de dependência para criar a instância como singleton. Essa abordagem garante que teremos uma instância apenas de HttpClient, deixando o gerenciamento de sockets como responsabilidade desta única instância. Contudo, embora seja uma implementação válida, essa não é a melhor alternativa, pois o client não funcionará da forma esperada em caso de alteração do DNS.

O que fazer para solucionar o problema?

No .NET Core 2.1 foi introduzida a classe DefaultHttpClientFactory para construção de instâncias de HttpClient. Essa implementação obedece o contrato IHttpClientFactory e pode ser integrado com o  pacote Microsoft.Extensions.DependencyInjection.

Podemos utilizar o IHttpClientFactory de 4 formas distintas, sendo elas:

  • Basic usage: o IHttpClientFactory é injetado diretamente no construtor, onde é utilizado o método CreateClient para obter uma instância HttpClient;
  • Named Clients: instâncias HttpClient são nomeadas e definidas na configuração do contêiner de injeção de dependências, sendo criadas através de seus alias pelo método CreateClient do IHttpClientFactory;
  • Generated Clients: faz uso do IHttpClientFactory em combinação com bibliotecas de terceiros;
  • Typed Clients: essa abordagem traz consigo o benefício de poder injetar diretamente o HttpClient na classe desejada.

Dentre as abordagens citadas, recomendamos a abordagem de typed clients, pois, conforme mencionado acima, ela possibilita a injeção direta do HttpClient, além de prover os mesmos recursos da Named Clients, sem a necessidade de utilizar strings como chaves, possibilitando o encapsulamento da lógica de consumo dos endpoints em serviços isolados. Entretanto, vale ressaltar que essa abordagem depende da integração com o DI da Microsoft, através do método de extensão AddHttpClient. Basicamente, o seu funcionamento consiste em deixar o contêiner de DI realizar a criação do HttpClient e injetar a instância dentro de uma classe de serviço.

Na prática, precisamos apenas de uma classe que receberá, por injeção de dependência, uma instância HttpClient, como o código abaixo demonstra:

public sealed class CorrectPatternHttpService : ICorrectPatternHttpService
{
    private readonly HttpClient _httpClient;

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

    public async Task<IEnumerable<SpaceflightNewDTO>> GetUsingCorrectPatternAsync()
    {           
        var response = await _httpClient.GetAsync("https://api.spaceflightnewsapi.net/v3/articles");

        var serializer = new JsonSerializer();
        using var stream = await response.Content.ReadAsStreamAsync();
        using var streamReader = new StreamReader(stream);
        using var jsonTextReader = new JsonTextReader(streamReader);
        return serializer.Deserialize<IEnumerable<SpaceflightNewDTO>>(jsonTextReader);
    }
}

Por fim, o código abaixo, demonstra a configuração dentro do contêiner de DI que permite que o IHttpClientFactory faça o trabalho de gerenciar as instâncias de HttpClient:

internal static IServiceCollection AddHttpClients(this IServiceCollection services)
{
    services.AddHttpClient<ICorrectPatternHttpService, CorrectPatternHttpService>();
    return services;
}

Conclusão

A utilização equivocada do HttpClient para realizar integrações entre sistemas via HTTP, pode causar diferentes tipos de problemas e trazer enormes dores de cabeça. Portanto, conhecer a melhor abordagem para a realização de requisições HTTP não é negociável, mas sim imprescindível nos dias de hoje.

Compartilhe este insight:

Comentários

Participe deixando seu comentário sobre este artigo a seguir:

Subscribe
Notify of
guest
0 Comentários
Inline Feedbacks
View all comments

AUTOR

Francisco Schneider
Desenvolvedor especialista em .NET com experiência em aplicações corporativas complexas.

SOLUÇÕES EXIMIACO

Codificação de Software

ESTRATÉGIA & EXECUÇÃO EM TI

Simplificamos, potencializamos 
aceleramos resultados usando a tecnologia do jeito certo.

INSIGHTS EXIMIACO

Confira outros insights de nossos consultores relacionados a esta solução de negócio:

13/07/2021
13/07
2021
09/07/2021
Raphael Castilho
Desenvolvedor Especialista em aplicações corporativas .NET
09/07
2021
25/05/2021
25/05
2021

COMO PODEMOS LHE AJUDAR?

Vamos marcar uma conversa para que possamos entender melhor sua situação e juntos avaliar de que forma a tecnologia pode trazer mais resultados para o seu negócio.

COMO PODEMOS LHE AJUDAR?

Vamos marcar uma conversa para que possamos entender melhor sua situação e juntos avaliar de que forma a tecnologia pode trazer mais resultados para o seu negócio.

+55 51 3049-7890 |  [email protected]

0
Queremos saber a sua opinião, deixe seu comentáriox
()
x

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

O seu insight foi excluído com sucesso!

O seu insight foi excluído e não está mais disponível.

O seu insight foi salvo com sucesso!

Ele está na fila de espera, aguardando ser revisado para ter sua publicação programada.