Para hablar con una API desde C# no hace falta ninguna biblioteca: HttpClient viene con .NET y, con las extensiones de System.Net.Http.Json, pide, envía y convierte JSON en una sola línea. Lo que sí conviene saber es cuándo lanza excepciones y cuándo no.
También te puede interesar
El cliente: uno para todo el programa
Todo pasa por HttpClient, del espacio de nombres System.Net.Http, que está incluido en .NET: no hay que instalar nada. La regla más importante es que se crea una vez y se reutiliza. Cada HttpClient mantiene sus propias conexiones abiertas, y crear uno nuevo por cada petición agota los puertos del sistema en un programa que hace muchas. En un programa corto como estos, uno al principio basta; en una aplicación web, se pide con IHttpClientFactory.
GET y la respuesta convertida en objetos
GetFromJsonAsync hace la petición, comprueba el código de estado y convierte el JSON en objetos de tus tipos, todo en una línea. Los record del final describen la forma del JSON que devuelve https://httpbin.org/json, un servicio público de pruebas:
#:property PublishAot=false
using System.Net.Http.Json;
using var cliente = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
var datos = await cliente.GetFromJsonAsync<Respuesta>("https://httpbin.org/json");
Console.WriteLine(datos!.Slideshow.Title);
Console.WriteLine($"de {datos.Slideshow.Author}, {datos.Slideshow.Slides.Count} diapositivas");
foreach (var d in datos.Slideshow.Slides)
{
Console.WriteLine($" - {d.Title}");
}
record Respuesta(Presentacion Slideshow);
record Presentacion(string Title, string Author, List<Diapositiva> Slides);
record Diapositiva(string Title, string Type);salidaSample Slide Show de Yours Truly, 2 diapositivas - Wake up to WonderWidgets! - Overview
La línea #:property PublishAot=false solo hace falta en un archivo suelto ejecutado con dotnet run: .NET 10 los compila con AOT, que desactiva la conversión de JSON por reflexión. Lo explica la página de JSON en C#. El JSON trae los nombres en minúscula ("title") y los records en mayúscula (Title): GetFromJsonAsync usa las opciones «web» de System.Text.Json, que no distinguen mayúsculas. Las propiedades del JSON que no están en el record, como "date", se ignoran. Las instrucciones van con await porque una petición tarda; el programa queda libre mientras espera (lo explica la lección de LINQ y async).
POST con un cuerpo JSON
#:property PublishAot=false
using System.Net.Http.Json;
using System.Text.Json;
using var cliente = new HttpClient();
var nueva = new { Nombre = "Ana", Edad = 31 };
using var respuesta = await cliente.PostAsJsonAsync("https://httpbin.org/post", nueva);
Console.WriteLine($"{(int)respuesta.StatusCode} {respuesta.StatusCode}");
Console.WriteLine(respuesta.Content.Headers.ContentType);
var eco = await respuesta.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(JsonSerializer.Serialize(eco.GetProperty("json")));salida200 OK
application/json
{"edad":31,"nombre":"Ana"}PostAsJsonAsync convierte el objeto en JSON, con los nombres en camelCase (nombre, no Nombre), y pone la cabecera Content-Type. httpbin devuelve lo que recibió en la propiedad json, y por eso aparece el mismo objeto. Cuando no quieres declarar un tipo para la respuesta, JsonElement permite recorrerla a mano.
Un 404 no es una excepción
using var cliente = new HttpClient();
using var respuesta = await cliente.GetAsync("https://httpbin.org/status/404");
Console.WriteLine($"Llegó la respuesta: {(int)respuesta.StatusCode}, ¿éxito? {respuesta.IsSuccessStatusCode}");
try
{
respuesta.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Error {(int?)ex.StatusCode}: {ex.Message}");
}salidaLlegó la respuesta: 404, ¿éxito? False Error 404: Response status code does not indicate success: 404 (NOT FOUND).
Con GetAsync o SendAsync, una respuesta 404 o 500 llega con normalidad: el servidor contestó, y lo que dice está en StatusCode. Tienes que comprobarlo tú con IsSuccessStatusCode o convertirlo en excepción con EnsureSuccessStatusCode(). Los métodos cómodos (GetStringAsync, GetFromJsonAsync) sí lanzan HttpRequestException si el código no es 2xx. Los mensajes de las excepciones de .NET salen en inglés.
El tiempo de espera
using var cliente = new HttpClient { Timeout = TimeSpan.FromSeconds(1) };
try
{
// /delay/3 tarda tres segundos en contestar
string texto = await cliente.GetStringAsync("https://httpbin.org/delay/3");
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}salidaThe request was canceled due to the configured HttpClient.Timeout of 1 seconds elapsing.
Timeout, HttpClient espera 100 segundos a un servidor que no contesta. Y cuando se agota, no lanza TimeoutException sino TaskCanceledException, la misma que si cancelas tú la petición: atrapa esa. Para tiempos distintos en cada petición, usa un CancellationTokenSource con CancelAfter y pásalo al método.Resumen
| Para | Usa |
|---|---|
| Pedir JSON y convertirlo | await cliente.GetFromJsonAsync<T>(url) |
| Enviar JSON | await cliente.PostAsJsonAsync(url, objeto) |
| Mirar el código de estado | GetAsync y respuesta.StatusCode |
| Fallar si no es 2xx | respuesta.EnsureSuccessStatusCode() |
| Limitar la espera | Timeout o un CancellationToken |
Todo el código de esta página se compiló con el .NET SDK 10.0.403 (C# 14) y se ejecutó con .NET 10.0.14 en un contenedor limpio antes de publicarla; las salidas y los mensajes del compilador están copiados de esa ejecución.