Introducción: ¿por qué necesitas un cliente HTTP correcto en C#?
En el desarrollo moderno, prácticamente ninguna aplicación prescinde de la interacción con servicios externos. Ya sea enviar datos a un servidor, obtener el tipo de cambio o trabajar con una API en la nube, todo requiere una implementación adecuada de solicitudes HTTP. En el ecosistema de C# (.NET), la herramienta principal para esta tarea es la clase HttpClient. Sin embargo, muchos desarrolladores, especialmente los principiantes, cometen errores típicos: crean una nueva instancia de HttpClient para cada solicitud, olvidan manejar excepciones o trabajan incorrectamente con la asincronía.
En este artículo, analizaremos cómo organizar correctamente un cliente HTTP en C#, veremos el envío de solicitudes GET y POST, el trabajo con encabezados y el cuerpo de la solicitud, así como las mejores prácticas para el manejo de errores. Después de leerlo, podrás escribir código robusto y eficiente para interactuar con cualquier API REST.
Fundamentos del trabajo con HttpClient en C#
La clase HttpClient pertenece al espacio de nombres System.Net.Http y está disponible en todas las versiones modernas de .NET (Core, 5, 6, 7, 8 y superiores). Su principal ventaja es el soporte para operaciones asíncronas y la gestión automática del pool de conexiones.
Creación correcta de HttpClient
Regla fundamental: no crees un nuevo HttpClient para cada solicitud. Esto puede provocar el agotamiento de sockets (socket exhaustion). En su lugar, utiliza una única instancia durante toda la vida de la aplicación o emplea IHttpClientFactory.
// ❌ Mal: nuevo HttpClient para cada solicitudusing (var client = new HttpClient()){ var response = await client.GetAsync("https://api.example.com");}
// ✅ Bien: un solo HttpClient para todas las solicitudespublic class MyHttpService{ private static readonly HttpClient _client = new HttpClient(); public async Task<string> GetDataAsync(string url) { return await _client.GetStringAsync(url); }}Configuraciones básicas
Al inicializar HttpClient, se pueden establecer la dirección base, los tiempos de espera y los encabezados predeterminados:
public class ApiClient{ private readonly HttpClient _httpClient; public ApiClient() { _httpClient = new HttpClient { BaseAddress = new Uri("https://api.pythonlib.ru/"), Timeout = TimeSpan.FromSeconds(10) }; // Encabezados que se enviarán con cada solicitud _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0"); }}Envío de solicitudes GET y POST
Veamos los dos tipos más comunes de solicitudes HTTP: obtener datos (GET) y enviar datos (POST).
Solicitud GET: obtener datos
La solicitud GET se utiliza para leer datos del servidor. No debe modificar el estado del recurso y no contiene cuerpo.
public async Task<string> GetUserAsync(int userId){ try { var response = await _httpClient.GetAsync($"/api/users/{userId}"); response.EnsureSuccessStatusCode(); // Lanzará una excepción en caso de error return await response.Content.ReadAsStringAsync(); } catch (HttpRequestException ex) { Console.WriteLine($"Error de solicitud: {ex.Message}"); throw; }}Para casos simples, se puede usar la forma abreviada:
string json = await _httpClient.GetStringAsync("/api/users/42");Solicitud POST: enviar datos
La solicitud POST se utiliza para crear nuevos recursos. Los datos se envían en el cuerpo de la solicitud, generalmente en formato JSON.
public async Task<User> CreateUserAsync(User newUser){ // Serializamos el objeto a JSON var json = JsonSerializer.Serialize(newUser);