Introducción: ¿por qué escribir un cliente HTTP en Rust?
El desarrollo moderno rara vez prescinde de la interacción con servicios externos. Ya sea una API REST del clima, una base de datos o una arquitectura de microservicios, saber enviar peticiones HTTP es una habilidad básica. Rust, con su enfoque en el rendimiento y la seguridad, ofrece varias soluciones elegantes para trabajar con HTTP. En este artículo, analizaremos cómo crear correctamente clientes HTTP en Rust: desde la elección de la librería hasta el manejo de escenarios complejos.
Veremos las dos librerías más populares: reqwest (estándar asíncrono) y ureq (enfoque síncrono simple). Aprenderá a enviar peticiones GET y POST, procesar JSON, gestionar cabeceras y evitar errores típicos.
1. Elección de la librería: reqwest vs ureq
Antes de escribir código, hay que decidir qué herramienta usar. En el ecosistema de Rust hay dos candidatos principales.
1.1. Reqwest — potente y asíncrono
reqwest es el estándar de facto para peticiones HTTP en Rust. Está construido sobre hyper y tokio, soporta asincronía, serialización/deserialización automática de JSON y trabajo con cookies. Ideal para aplicaciones de alta carga y servidores web.
Para empezar, agregue en Cargo.toml:
[dependencies]reqwest = { version = "0.12", features = ["json"] }tokio = { version = "1", features = ["full"] }serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"1.2. Ureq — simplicidad sin asincronía
Si no necesita llamadas asíncronas y está escribiendo un script simple o una utilidad CLI, ureq será una excelente opción. Es síncrono, minimalista y no requiere una configuración compleja del runtime.
[dependencies]ureq = "2.9"serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"¿Cuándo elegir qué? Para servidores web y aplicaciones complejas — reqwest. Para tareas simples, pruebas y scripts — ureq.
2. Peticiones básicas: GET y POST
Veamos ejemplos prácticos para ambas librerías.
2.1. Petición GET con reqwest
El ejemplo más simple: obtener datos de una API pública. Usamos una función main asíncrona con la macro #[tokio::main].
use reqwest;use std::error::Error;
#[tokio::main]async fn main() -> Result<(), Box<dyn Error>> { let url = "https://jsonplaceholder.typicode.com/posts/1"; let response = reqwest::get(url).await?;
// Verificamos el estado de la respuesta if response.status().is_success() { let body = response.text().await?; println!("Respuesta: {}", body); } else { eprintln!("Error HTTP: {}", response.status()); }
Ok(())}2.2. Petición POST con cuerpo JSON (reqwest)
El envío de datos a menudo requiere serializar una estructura. Para ello usamos la crate serde.
use reqwest;use serde::{Deserialize, Serialize};use std::error::Error;
#[derive(Serialize, Deserialize, Debug)]struct Post { title: String, body: String, user_id: i32,}
#[tokio::main]async fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::Client::new(); let new_post = Post { title: "Hola Rust".to_string(), body: "Este es el cuerpo de la petición".to_string(), user_id: 1, };
let response = client .post("https://jsonplaceholder.typicode.com/posts") .json(&new_post) .send() .await?;
let created_post: Post = response.json().await?; println!("Post creado: {:?}", created_post);
Ok(())}2.3. GET simple con ureq
Con ureq el código se vuelve más corto, ya que no requiere asincronía:
use ureq;use std::error::Error;
fn