Introducción: por qué es importante un cliente HTTP correcto en C#
En el desarrollo moderno con C# (.NET), trabajar con solicitudes HTTP es una necesidad básica. Ya sea para acceder a una REST API, descargar datos de un servidor web o integrarse con servicios en la nube, no se puede prescindir de un cliente HTTP confiable. Sin embargo, muchos desarrolladores, especialmente los principiantes, cometen errores típicos: gestión incorrecta de conexiones, ignorar tiempos de espera o un manejo ineficiente de las respuestas. En este artículo, analizaremos la creación de un cliente HTTP en C# desde lo básico hasta técnicas avanzadas.
Veremos la clase HttpClient, la herramienta principal para trabajar con HTTP en .NET, así como enfoques alternativos. Aprenderá cómo enviar correctamente solicitudes GET y POST, manejar JSON, gestionar encabezados y evitar problemas comunes de rendimiento.
Fundamentos: creación y configuración de HttpClient
Inicialización correcta de HttpClient
La clase HttpClient está diseñada para ser reutilizada. Crear una nueva instancia para cada solicitud es un antipatrón que puede provocar el agotamiento de los sockets. En su lugar, utilice una única instancia para toda la aplicación o inyección de dependencias (DI) a través de IHttpClientFactory.
// Incorrecto: crear una instancia para cada solicitudpublic async Task<string> BadRequest(string url){ using (var client = new HttpClient()) { return await client.GetStringAsync(url); }}
// Correcto: instancia estáticapublic static readonly HttpClient SharedClient = new HttpClient();
// O a través de IHttpClientFactory (recomendado para ASP.NET Core)services.AddHttpClient("MyClient", client =>{ client.BaseAddress = new Uri("https://api.example.com"); client.DefaultRequestHeaders.Add("Accept", "application/json");});Configuración de tiempos de espera y dirección base
Los tiempos de espera protegen la aplicación de bloqueos cuando el servidor no está disponible. HttpClient tiene su propio tiempo de espera, pero se puede sobrescribir.
HttpClient client = new HttpClient{ BaseAddress = new Uri("https://jsonplaceholder.typicode.com"), Timeout = TimeSpan.FromSeconds(10) // 10 segundos para toda la solicitud};También es útil establecer MaxResponseContentBufferSize para limitar el tamaño de los datos recibidos.
Envío de solicitudes: GET, POST, PUT y DELETE
Solicitud GET: obtención de datos
La forma más sencilla de obtener datos es mediante GetStringAsync o GetAsync con manejo manual.
public async Task<string> GetUsersAsync(){ HttpResponseMessage response = await SharedClient.GetAsync("/users"); response.EnsureSuccessStatusCode(); // lanza una excepción en caso de error return await response.Content.ReadAsStringAsync();}
// O aún más simple:string json = await SharedClient.GetStringAsync("/users");Solicitud POST: envío de datos
Para enviar JSON, use PostAsJsonAsync (requiere el paquete System.Net.Http.Json).
var newUser = new { name = "Iván", email = "ivan@example.com" };HttpResponseMessage response = await SharedClient.PostAsJsonAsync("/users", newUser);if (response.IsSuccessStatusCode){ var createdUser = await response.Content.ReadFromJsonAsync<User>();}Para enviar un formulario, use FormUrlEncodedContent.
var formData = new Dictionary<string, string>{ { "username", "admin" }, { "password", "secret" }};var content = new FormUrlEncodedContent(formData);HttpResponseMessage response = await SharedClient.PostAsync("/login", content);Solicitudes PUT y DELETE
La actualización y eliminación se realizan de manera similar:
// PUTvar updateData = new { id = 1, name = "Nombre actualizado" };HttpResponseMessage putR