Cliente HTTP em C#: fazendo requisições corretamente — nível avançado
No desenvolvimento moderno em .NET, o trabalho com requisições HTTP vai muito além da simples chamada de WebClient ou do básico HttpClient. Em nível avançado, é necessário considerar o gerenciamento do pool de conexões, o tratamento correto de erros, timeouts, políticas de repetição e execução assíncrona. Neste artigo, abordaremos as melhores práticas de trabalho com o cliente HTTP em C#, que ajudarão você a criar aplicações robustas e de alto desempenho.
1. Trabalho correto com HttpClient e pool de conexões
Um dos erros mais comuns é criar uma nova instância de HttpClient para cada requisição. Isso leva ao esgotamento de sockets (socket exhaustion) porque cada instância mantém uma conexão TCP aberta no estado TIME_WAIT.
Incorreto:
public async Task<string> GetDataAsync(string url){ using (var client = new HttpClient()) { return await client.GetStringAsync(url); }}Correto: use uma única instância estática ou singleton de HttpClient.
public class HttpService{ private static readonly HttpClient _client = new HttpClient();
public async Task<string> GetDataAsync(string url) { return await _client.GetStringAsync(url); }}Para um controle mais refinado, use IHttpClientFactory do pacote Microsoft.Extensions.Http. A fábrica gerencia o pool e reconecta automaticamente em caso de falhas de DNS.
// Em Startup.cs ou Program.csservices.AddHttpClient("MyClient", client =>{ client.BaseAddress = new Uri("https://api.example.com"); client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");});
// No serviçopublic class MyService{ private readonly IHttpClientFactory _httpClientFactory;
public MyService(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; }
public async Task<string> GetDataAsync() { var client = _httpClientFactory.CreateClient("MyClient"); return await client.GetStringAsync("/data"); }}2. Timeouts e cancelamento de requisições
Sem timeouts, seu cliente pode travar por tempo indeterminado. No HttpClient, existem dois mecanismos:
- Timeout — timeout geral para toda a requisição.
- CancellationToken — para cancelar a requisição externamente (por exemplo, o usuário fechou o aplicativo).
Defina o Timeout de forma razoável. Para a maioria das APIs, 10–30 segundos são adequados.
var client = new HttpClient{ Timeout = TimeSpan.FromSeconds(15)};
// Exemplo com CancellationTokenSourceusing var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));try{ var response = await client.GetAsync("https://slow-api.example.com", cts.Token);}catch (TaskCanceledException){ Console.WriteLine("Requisição cancelada por timeout.");}Importante: Timeout e CancellationToken funcionam em conjunto. Se o CancellationToken disparar primeiro, a requisição é cancelada mais rapidamente.
3. Tratamento de erros e política de repetição (Retry)
Falhas de rede e erros temporários do servidor são comuns. Em vez de falhar imediatamente, implemente uma política de repetição com backoff exponencial. Para isso, a biblioteca Polly é excelente.
using Polly;using Polly.Extensions.Http;
// Definindo a política de repetiçãovar retryPolicy = HttpPolicyExtensions .HandleTransientHttpError() // 5xx, 408 .Or<HttpRequestException>() .WaitAndRetryAsync(3, retryAttempt => Tim