Skip to main content

IHttpClientFactory | CSharp

  • IHttpClientFactory | CSharp

Visão Geral

Tradicionalmente, em aplicações .NET ao fazer chamadas HTTP geralmente se utilizava diretamente a classe HttpClient. Isso costuma gerar problemas de desempenho e de gerenciamento, como socket exhaustion (exaustão de soquetes). O IHttpClientFactory, introduzido no .NET Core 2.1, fornece uma forma centralizada e mais segura de configurar e criar instâncias de HttpClient, gerenciando corretamente o ciclo de vida internamente.


Por que usar IHttpClientFactory?

Principais problemas que ele resolve

  1. Socket Exhaustion: Criar muitas instâncias de HttpClient, ou não reutilizar/injectar corretamente, pode levar ao esgotamento de soquetes. O HttpClient pode manter conexões subjacentes vivas e não fechadas. IHttpClientFactory cuida dos handlers por trás para evitar esse tipo de problema.
  2. Configuração centralizada: Permite definir configurações (base address, headers, timeout) em um único local, para clientes HttpClient em diferentes cenários.
  3. Clientes nomeados (Named) e tipados (Typed): Permite definir diferentes configurações para diferentes finalidades — e injetar de forma adequada, ao invés de criar manualmente cada HttpClient.

Três formas de uso

O artigo apresenta 3 formas de usar IHttpClientFactory. Vamos documentar cada uma.

1. Uso simples – injetando IHttpClientFactory

Registro (por exemplo em Startup.cs ou Program.cs dependendo da versão .NET):

public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
}

Uso na classe de serviço:

public class WeatherService
{
private readonly IHttpClientFactory _httpClientFactory;

public WeatherService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}

public async Task<string> GetWeatherDataAsync(string city, string apiKey)
{
var httpClient = _httpClientFactory.CreateClient();
var url = $"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={apiKey}";
var response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return null;
}
}

Observações:

  • Esse método permite reutilização automática do HttpClient criado, conforme a fábrica gerencia internamente.
  • A URL completa foi usada na chamada (BaseAddress não foi definida no cliente).
  • Quando há poucas configurações diferentes, pode ser suficiente.

2. Named Clients (Clientes nomeados)

Permite registrar clientes HttpClient com nomes específicos, cada um com configuração própria.

Registro:

public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("WeatherClient", client =>
{
client.BaseAddress = new Uri("https://api.openweathermap.org/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
}

Uso:

public class WeatherService
{
private readonly IHttpClientFactory _httpClientFactory;

public WeatherService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}

public async Task<string> GetWeatherDataAsync(string city, string apiKey)
{
var httpClient = _httpClientFactory.CreateClient("WeatherClient");
var url = $"data/2.5/weather?q={city}&appid={apiKey}";
var response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return null;
}
}

Vantagens:

  • Configuração central (“WeatherClient”) tem BaseAddress, headers padronizados.
  • O código de serviço reduz duplicação de URL base ou headers.
  • Se tiver várias APIs ou clientes HTTP diferentes, cada um pode ter seu nome/configuração.

3. Typed Clients (Clientes tipados)

Aqui você define uma classe que encapsula o HttpClient específico, e registra esse cliente como um tipo no DI.

Definição da classe cliente:

public class WeatherClient
{
private readonly HttpClient _httpClient;

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

public async Task<string> GetWeatherDataAsync(string city, string apiKey)
{
var url = $"data/2.5/weather?q={city}&appid={apiKey}";
var response = await _httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return null;
}
}

Registro no DI:

public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<WeatherClient>(client =>
{
client.BaseAddress = new Uri("https://api.openweathermap.org/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
}

Uso da classe cliente em outro serviço:

public class WeatherService
{
private readonly WeatherClient _weatherClient;

public WeatherService(WeatherClient weatherClient)
{
_weatherClient = weatherClient;
}

public async Task<string> GetWeatherDataAsync(string city, string apiKey)
{
return await _weatherClient.GetWeatherDataAsync(city, apiKey);
}
}

Benefícios:

  • Encapsula a lógica HTTP específica para uma API ou serviço.
  • Permite melhor separação de responsabilidades.
  • Facilita testes e manutenção.
  • Fornece configuração central como Named, mas com tipo forte.

Avisos / Boas práticas

  • Evite injetar clientes tipados em serviços que tenham tempo de vida Singleton (singleton). O HttpClient pode não se comportar corretamente se for usado como singleton de forma errada.
  • A fábrica gerencia internamente os handlers e pools de conexões. Não basta usar using (new HttpClient()) repetidamente — isso pode causar desgaste de recursos.
  • Configure adequadamente o BaseAddress, DefaultRequestHeaders, Timeout, PolicyHandlers (ex: retry, circuit breaker) se necessário.
  • Nomear ou tipar clientes traz clareza e consistência — especialmente em aplicações com múltiplas APIs externas.
  • Para cenários simples, pode bastar o primeiro modelo (injeção de IHttpClientFactory), mas para aplicações maiores vale adotar Named ou Typed.

Resumo

  • IHttpClientFactory é o “caminho recomendado” para criar e gerenciar instâncias HttpClient no .NET moderno.
  • Três formas principais: injetar IHttpClientFactory, usar Named Clients, usar Typed Clients.
  • Ao aplicar essas abordagens corretamente, você obtém: melhor consumo de recursos, menos risco de exaustão de soquetes, configuração centralizada, maior manutenibilidade.
  • Evite misturar lifetime de clientes tipados com serviços singleton de forma inadequada.