Cliente HTTP en C#: haciendo solicitudes correctamente — nivel avanzado
En el desarrollo moderno con .NET, trabajar con solicitudes HTTP va mucho más allá de simplemente llamar a WebClient o al HttpClient básico. A nivel avanzado, es necesario considerar la gestión del pool de conexiones, el manejo correcto de errores, los tiempos de espera, las políticas de reintentos y la ejecución asíncrona. En este artículo, analizaremos las mejores prácticas para trabajar con el cliente HTTP en C# que le ayudarán a crear aplicaciones robustas y de alto rendimiento.
1. Trabajo correcto con HttpClient y el pool de conexiones
Uno de los errores más comunes es crear una nueva instancia de HttpClient para cada solicitud. Esto provoca el agotamiento de sockets (socket exhaustion) porque cada instancia mantiene una conexión TCP abierta en estado TIME_WAIT.
Incorrecto:
public async Task<string> GetDataAsync(string url){ using (var client = new HttpClient()) { return await client.GetStringAsync(url); }}Correcto: use una única instancia estática o 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 un control más preciso, use IHttpClientFactory del paquete Microsoft.Extensions.Http. La fábrica gestiona el pool y restablece automáticamente las conexiones en caso de fallos de DNS.
// En Startup.cs o Program.csservices.AddHttpClient("MyClient", client =>{ client.BaseAddress = new Uri("https://api.example.com"); client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");});
// En el serviciopublic 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. Tiempos de espera y cancelación de solicitudes
Sin tiempos de espera, su cliente podría quedarse bloqueado indefinidamente. HttpClient tiene dos mecanismos:
- Timeout — tiempo de espera general para toda la solicitud.
- CancellationToken — para cancelar la solicitud desde el exterior (por ejemplo, el usuario cerró la aplicación).
Establezca Timeout de manera razonable. Para la mayoría de las API, 10–30 segundos es adecuado.
var client = new HttpClient{ Timeout = TimeSpan.FromSeconds(15)};
// Ejemplo con 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("Solicitud cancelada por tiempo de espera.");}Importante: Timeout y CancellationToken trabajan juntos. Si CancellationToken se activa antes, la solicitud se cancela más rápido.
3. Manejo de errores y política de reintentos (Retry)
Los fallos de red y los errores temporales del servidor son algo común. En lugar de fallar inmediatamente, implemente una política de reintentos con espera exponencial. Para esto, la biblioteca Polly es ideal.
using Polly;using Polly.Extensions.Http;
// Definimos la política de reintentosvar retryPolicy = HttpPolicyExtensions .HandleTransientHttpError() // 5xx, 408 .Or<HttpRequestException>() .WaitAndRetryAsync(3, retryAttempt => Tim