Rusty Full Stack

El blog para los amantes de Rust, Ionic y Vuejs

Jaime Blandón
Jaime Blandón Desarrollador de software desde 2009, entusiasta de Rust, Vuejs y Ionic!. Fundador de este blog, espero que las publicaciones te sean de utilidad y si tienes comentarios para mejorar, son bienvenidos.

Peticiones HTTP y Clientes API Con Rust

En esta publicación vamos a explorar como hacer peticiones HTTP e invocar APIs utilizando Rust.

¿Para qué sirven las peticiones HTTP?

El Hypertext Transfer Protocol o HTTP es un protocolo sin el cual el internet simplemente no existiría ya que es el protocolo o medio por el cual existen el intercambio de documentos de hiper-texto (como un link) en el internet.

Lo importante a destacar en el párrafo anterior es el "intercambio" de información o datos. Como un ejemplo, imagina el navegador web que estás utilizando para leer este artículo, cuando decidiste abrir el link que te trajo hasta acá, hubo una petición de tu navegador al servidor donde se encuentra alojado rustyfullstack, el servidor de rustyfullstack, respondió a tu navegador con el hiper-texto requerido para cargar esta página y que tu navegador pueda mostrarte todo lo que estas leyendo y viendo ahora mismo en tu browser 😁.

El propósito de este artículo, mas que hablar sobre http, es el aprender a utilizarlo con Rust, por lo que si quieres aprender más sobre HTTP, sus diferentes status, su descripción, etc, puedes leer mucha más información acá:

https://developer.mozilla.org/en-US/docs/Web/HTTP

¿Qué es un API y Para Qué Sirve?

Una application programming interface o API, es una manera en que dos o más programas de computadoras pueden comunicarse entre si.

Imagina que estás construyendo un programa en Rust en el cual necesitas pedir o consumir información que se genera en otro programa que puede estar hecho en Rust o incluso otro lenguaje de programación y que a su vez no te permita, por motivos de seguridad, distancia o lo que sea, ingresar directamente a su base de datos, es justamente para estos escenarios que las API resultan bastante útiles.

En las API se establece un contrato o formato para solicitar la información desde un programa (cliente) que consuma una respuesta de otro (servicio). Las respuestas a su vez tienen un formato que generalmente utiliza formato de texto/json para representar la información necesaria por el cliente.

Al manejar la comunicación entre programas a través de APIs permite crear arquitecturas flexibles, re-utilizables, dependiendo del diseño, más seguras que ingresar directamente a una base de datos, etc.

Hoy en día está muy de moda que estas API utilicen HTTP como medio de comunicación pues utilizando permite utilizar un estándar independiente del lenguaje de programación en que tanto el cliente como el servicio se encuentre hecha.

Igual que con la sección anterior de HTTP, no es el propósito de este artículo extendernos mucho sorbe qué es un API o cómo crear un servicio (pero si lo dejas la caja de comentarios podemos extendernos mucho más en futuros artículos! 😎) así que por ahora vamos a centrarnos en cómo consumir los API, si quieres más información puedes leer este artículo.

¿Qué es API? Ejemplos, ventajas y tipos.

Reqwest Para Hacer Peticiones HTTP con Rust?

En la gran mayoría de lenguajes de programación modernos, existen librerías o paquetes para seguir el protocolo HTTP y poder comunicarse con las API. En el caso de Rust, también existen muchos crates que permiten comunicarse con un API, una de mis favoritas se llama reqwest.

Algunos features que me gustan mucho de reqwest son:

  • Trabaja de forma síncrona o asíncrona.
  • Múltiples mecanismos para hacer requests o armar el "body" de un request.
  • Utiliza certificados o TLS nativos para comunicarse no solamente vía HTTP, sino también vía HTTPS.
  • Reconocimiento y utilización de Cookies.
  • Se pueden configurar o personalizar las políticas de redireccionamiento.
  • Su documentación es bastante clara.
  • Métodos de seguridad integrada como autenticación básica y/o bearer tokens.
  • Configuración de "Headers" cuando es requerido por el API.

Ahora veamos como utilizarlo, empecemos con un ejemplo bastante simple, hagamos un programa para poder obtener el código HTML de la página de inicio de Google.

Primero creemos un nuevo proyecto en la carpeta de tu preferencia digita el siguiente comando en una terminal.

cargo new http-get-google

cd http-get-google

Ahora vamos a abrir el archivo Cargo.toml y agregar el siguiente código:

        
        
            // Cargo.toml

[package]
name = "http-get-google"
version = "0.1.0"
edition = "2021"
 
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
[dependencies]
reqwest = "0.11.12"
tokio = {version = "1", features = ["full"]}
        
        
    

Es importante ver las dos dependencias que se agregan. La primera es evidentemente reqwest, la segunda es tokio, el cual es un crate para hacer que la función main pueda manejar características asíncronas, sin ellas utilizar palabras reservadas como await dentro de la función main no sería posible.

Ahora vamos a hacer que nuestro programa (cliente) solicite al servidor de google, que nos devuelva el HTML que conforma su pagina de inicio, para hacer esta solicitud (request) vamos a utilizar reqwest, en el archivo src/main.rs agrega el siguiente código:

        
        
            // src/main.rs

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let html: String = reqwest::get("https://www.google.com") // Se pasa el url o endpoint con el cual se interacturara
                        .await? // Hace que reqwest espere por la respuesta del servidor de Google
                        .text() //Convierte el resultado en un String
                        .await?;
    println!("{:?}", html);

    Ok(())
}
        
        
    

Algo a notar es que antes de definir la función main, estamos utilizando la línea

#[tokio::main]

Esto permite que la función main, pueda manejar features asíncronos utilizando la palabra reservada await.

Otra cosa a notar es como se define la función main:

async fn main()

Se antepone la palabra reservada async de forma explícita y como return value, es esperado ya sea algo vacio () o un error generico

Box<dyn std::error::Error. No siempre es bueno manejar errores genéricos, si te gustaría aprender a manejar los errores de una forma más personalizada déjalo en la casa de comentarios 😉

luego tenemos este bloque de código:

        
        
            
let html: String = reqwest::get("https://www.google.com") // Se pasa el url o endpoint con el cual se interacturara
                        .await? // Hace que reqwest espere por la respuesta del servidor de Google
                        .text() //Convierte el resultado en un String
                        .await?;
        
        
    

Esa línea pasa como parámetro la url o dirección web de la página de inicio de google y la función .text() convierte la respuesta del servidor de Google, en una String que luego imprimimos en la siguiente línea.

Si ahora corremos nuestro programa con:

cargo run

Veremos que en pantalla imprimirá el código HTML, en la siguiente imagen colocamos solo un fragmento ya que el HTML es bastante largo (por suerte los navegadores interpretan ese html por nosotros para mostrarnos algo que sí podemos entender a primera vista 🤩).

Genial! hemos hecho nuestra primera petición HTTP utilizando Rust. Sin embargo, todavía no hemos interactuado con un API ni hemos visto más detalles sobre cómo acceder a la metadata de la petición HTTP como por ejemplo el código o status de respuesta, los encabezados, y otros detalles. A continuación empezaremos a interactuar con un API y acceder a más detalles de la respuesta.

Tipos de Petición HTTP.

El protocolo HTTP tiene varios tipos de peticiones o requests, cada uno de estos tipos representa algún tipo de acción que queremos realizar, algunos de ellos son:

  • GET
  • POST
  • PUT
  • DELETE
  • PATCH
  • HEAD
  • Y muchos más que puedes encontrar acá.

Para los siguientes ejemplos nos vamos a enfocar en GET y POST porque son de los más comunes y también porque si aprendemos a hacerlo con ellos, prácticamente estamos aprendiendo a hacer requests para todos los demás tipos.

Peticiones GET Con Rust.

Ahora vamos a interactuar con un API haciendo una petición GET. Este tipo de peticiones generalmente se utilizan para solicitar o consultar datos de un servicio. Algunos API permiten incluso pasar parámetros para realizar algún tipo de filtro o acción que necesitemos del servicio, cuando se hace las peticiones con GET, generalmente estos parámetros se pasan por el URL del servicio, ya sea en forma de query params o en forma de parámetros de la url (Path params).

Para nuestro ejemplo vamos a solicitar al servicio de Google Translate (traductor de google) que nos devuelva todos los lenguajes que maneja, pero ¿dónde y cómo nos conectamos al servicio de google translate?

RapidAPI.

Existe un servicio en línea donde podemos encontrar (o incluso publicar 🥳) muchísimas API, algunas de ellas son gratuitas y algunas de paga, por suerte para nosotros, la que nos interesa es gratuita, es servicio se llama RapidAPI

Para poder utilizarlo podemos crear una cuenta totalmente GRATIS en su página de Sign Up, podemos utilizar nuestra cuenta de correo de Google, Facebook u otro tipo de email para suscribirnos.

Una vez suscritos, podemos buscar el API de Google translate, pero para facilitar el trabajo, puedes dar click a este link 😉

Deberías de ver la página sobre el API

Para poder hacer uso del API, debemos agregar la suscripción gratuita, para ello nos vamos en la sección de pricing

Y seleccionamos el plan Basic o gratuito que nos permitirá poder hacer hasta 500 requests mensuales, los suficientes para aprender sobre API con Rust 🤓

Si todo ha salido bien, deberías de ver la misma pantalla de la descripción sobre como utilizar el API, pero ahora te darás cuenta en el panel derecho que RapidAPI ha generado un token de autenticación que se utiliza para poder consultar el API, por motivos de seguridad, en la siguiente imagen he ocultado mi token.

Verás que el token de seguridad está con un encabezado llamado X-RapidAPI-Key, en un momento vamos a aprender a agregar estos encabezados en nuestro request, por el momento lo importante es suscribirse al api para poder utilizarlo, si ves en el panel del API también nos da el URL que podemos utilizar.

En la imagen anterior vemos un ejemplo sobre como utilizar el API con el lenguaje Node, pero nosotros lo aprenderemos con el lenguaje Rust, también si te fija en el panel izquierdo vemos diferentes Endpoints o url con los métodos HTTP que debemos utilizar.

En nuestro caso nos interesa el endpoint "languages" ya que es el único que utiliza el método GET.

Ahora si pasemos a construir un programa que utilice ese método GET y que solicite al API de google translate que le devuelva todos los lenguajes a los cuales puede hacer las traducciones.

Peticiones GET con Reqwest.

Empecemos creando un nuevo proyecto en el directorio de tu preferencia, para ello podemos utilizar el siguiente comando.

cargo new http-api-google-translate

Ahora nos movemos dentro del proyecto.

cd http-api-google-translate

También puedes encontrar todos los ejemplos de este artículo en el repositorio en github dando click acá.

Ahora vamos a empezar por configurar nuestro archivo Cargo.toml para agregar tanto el crate reqwest como tokio, pero ahora con una ligera diferencia ya que activaremos el feature JSON para reqwest, porque esta vez el API nos devolverá un JSON en lugar de un HTML como en el ejemplo anterior, editemos nuestro archivo Cargo.toml

        
        
            // Cargo.toml

[package]
name = "http-api-google-translate"
version = "0.1.0"
edition = "2021"
 
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
[dependencies]
reqwest = {version = "0.11.12", features = ["json"]}
tokio = {version = "1", features = ["full"]}
        
        
    

Ahora vamos a editar nuestro archivo src/main.rs para invocar el API, como mencionaba anteriormente vamos a necesitar pasarle el token de seguridad, pero por motivos didácticos, me gustaría que primero intentemos hacer un request al API pero sin el token y ver como se comporta la respuesta. Modifiquemos nuestro archivo src/main.rs

        
        
            // src/main.rs

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Una buena idea si se van hacer muchos requests
    // es crear un "Cliente" el cual se puede reutilizar
    // para varias peticiones
    let cliente = reqwest::Client::new();

    // Ahora las peticiones las podemos hacer con el ciente
    // en lugar de utilizar directamente el crate
    let url = "https://google-translate1.p.rapidapi.com/language/translate/v2/languages";

    let r = cliente.get(url)
            .send()
            .await?;
    
    // Ahora mostremos en pantalla la composicion completa de la respuesta.
    println!("Esta es la respuesta en crudo");
    println!("{:?}", r);

    Ok(())
}
        
        
    

Una diferencia es que en este código, en lugar de hacer un GET directamente con reqwest, estamos creando un objeto de tipo "Cliente", en la medida de lo posible, te recomiendo hacerlo así, ya que reutilizar el objeto cliente para varias peticiones, es un poco más óptimo en memoria y rendimiento de nuestro programa.

Ahora ejecutemos nuestro programa, si todo sale bien, debería de mostrar en pantalla el objeto o representación de la respuesta del servicio de google translate.

cargo run

Si todo ha salido bien debemos de ver una respuesta como esta:

Por si la imagen no se llegase a ver muy bien, el mensaje de respuesta es más o menos este:

        
        
            
Response { 
  url: Url { 
    scheme: "https", 
    cannot_be_a_base: false, 
    username: "", 
    password: None, 
    host: Some(Domain("google-translate1.p.rapidapi.com")), 
    port: None, 
    path: "/language/translate/v2/languages", 
    query: None, 
    fragment: None 
  }, 
  status: 401, 
  headers: {
    "date": "Thu, 20 Oct 2022 03:47:00 GMT", 
    "content-type": "application/json", 
    "transfer-encoding": "chunked", 
    "connection": "keep-alive", 
    "x-rapidapi-version": "1.2.8", 
    "x-rapidapi-region": "AWS - us-east-1", 
    "x-rapidapi-proxy-response": "true", 
    "server": "RapidAPI-1.2.8"
  }
}
        
        
    

La pregunta que debemos hacernos ahora es ¿qué significa esa respuesta?, ¿eso es lo que de verdad esperamos?, ¿qué pasa con el token de seguridad que debemos enviar?

Como respuestas a esas preguntas, lo que Sí hemos hecho es enviar un request o petición al API y este Sí nos ha contestado!! 😁

Pero también es verdad que la respuesta no es lo que esperamos porque falta el token de autenticación 🥺

Conocer el Status Code de Respuesta.

Para saber el por qué el request necesita el token de seguridad debemos mencionar que lo podemos saber en base al status code o el código de respuesta que el API nos ha enviado, el protocolo HTTP maneja códigos de respuesta estándares y que nos permiten identificar lo que el servicio quiere decirnos de forma estándar, sin necesidad de un traductor o de mucha documentación, para imprimir el código de respuesta o el status code modifiquemos un poco nuestro src/main.rs para que lo muestre en pantalla.

        
        
            // src/main.rs

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Una buena idea si se van hacer muchos requests
    // es crear un "Cliente" el cual se puede reutilizar
    // para varias peticiones
    let cliente = reqwest::Client::new();

    // Ahora las peticiones las podemos hacer con el ciente
    // en lugar de utilizar directamente el crate
    let url = "https://google-translate1.p.rapidapi.com/language/translate/v2/languages";

    let r = cliente.get(url)
            .send()
            .await?;
    
    // Ahora mostremos en pantalla la composicion completa de la respuesta.
    println!("Esta es la respuesta en crudo");
    println!("{:?}", r);

    // Mostremos el codigo de respuesta enviado por el servicio
    println!("Este es el status code retornado por el servicio");
    println!("{}", r.status().as_u16());
    Ok(())
}
        
        
    

Como puedes ver, con el bloque de código

        
        
            
r.status().as_u16()
        
        
    

Estamos obteniendo el código de respuesta del servicio, si corremos nuestro programa con:

cargo run

Veremos que al final nos muestra este mensaje:

Este es el status code retornado por el servicio

401

Como puedes ver el código es un 401, generalmente los códigos de error 401 y 403 son debido a falta de permisos para poder acceder a un recurso o acción, puedes ver el siguiente enlace con los códigos HTTP más utilizados para ver el significado de cada uno de ellos.

HTTP Response Status Code.

Al igual que ver el status code, también podemos ver a detalle los diferentes datos de la respuesta, para nuestro caso con el status code nos es suficiente, sin embargo si necesitas conocer más detalles como los encabezados de la respuesta, puedes ver esta parte de la documentación de reqwest.

Reqwest Response.

Agregando Encabezados (Headers) a Nuestro Request.

Ahora que hemos identificado que nos hace falta el token, debemos de enviarlo al servicio para que pueda devolvernos los datos que necesitamos.

Si vemos el ejemplo en Nodejs, vamos a necesitar los siguientes datos de encabezado.

  • Accept-Encoding
  • X-RapidAPI-Key (debes utilizar la tuya)
  • X-RapidAPI-Host

modifiquemos un poco nuestro código para agregar los headers, es importante que en el caso del X-RapidAPI-Key la reemplaces por la tuya, esto lo hacemos en el archivo src/main.rs.

        
        
            // src/main.rs

use reqwest::header;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configurando los headers necesarios
    let mut headers = header::HeaderMap::new();
    headers.insert("Accept-Encoding", header::HeaderValue::from_static("application/gzip"));
    // Reemplaza con tu propia key
    headers.insert("X-RapidAPI-Key", header::HeaderValue::from_static("<AGREGA ACA TU KEY>"));
    headers.insert("X-RapidAPI-Host", header::HeaderValue::from_static("google-translate1.p.rapidapi.com"));

    // Una buena idea si se van hacer muchos requests
    // es crear un "Cliente" el cual se puede reutilizar
    // para varias peticiones, ahora tambien agregamos los headers
    let cliente = reqwest::Client::builder()
        .default_headers(headers)
        .build()?;

    // Ahora las peticiones las podemos hacer con el ciente
    // en lugar de utilizar directamente el crate
    let url = "https://google-translate1.p.rapidapi.com/language/translate/v2/languages";

    let r = cliente.get(url)
            .send()
            .await?;
    
    // Ahora mostremos en pantalla la composicion completa de la respuesta.
    println!("Esta es la respuesta en crudo");
    println!("{:?}", r);

    // Mostremos el codigo de respuesta enviado por el servicio
    println!("Este es el status code retornado por el servicio");
    println!("{}", r.status().as_u16());
    Ok(())
}
        
        
    

En el código anterior recuerda reemplazar donde dice <AGREGA ACA TU KEY> por tu propio token.

Si ejecutamos nuevamente nuestro programa veremos que ahora el mensaje del status code pasará de 401 a un código 200 que significa que hemos tenido éxito

Este es el status code retornado por el servicio

200

Pero todavía la respuesta no parecer estar muy clara:

Veamos como hacerlo más claro para nosotros.

Manejo de Respuestas en Formato JSON.

En el blog tenemos una miniserie bastante interesante sobre el manejo de formato JSON con Rust, te recomendamos leer esta publicación si aún no lo has hecho:

Convertir un String en Formato JSON a un Objeto con Rust.

En esas publicaciones aprendemos sobre los crates llamados serde y serde_json, ambos crates facilitan muchísimo el manejo del formato JSON con Rust, ya que nos proveen con estructuras que podemos reutilizar.

Si bien el crate reqwest permite hacer una conversión directa a una estructura que definamos y que represente la respuesta, en algunas ocasiones, nos encontraremos con que no sabemos el formato correcto que esperamos, por lo que utilizar la estructura Value del crate serde_json, hace mucho sentido para poder hacer unas primeras exploraciones. Empecemos agregando serde_json a nuestro programa, esto lo hacemos en nuestro archivo Cargo.toml

        
        
            // Cargo.toml

[package]
name = "http-api-google-translate"
version = "0.1.0"
edition = "2021"
 
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
[dependencies]
reqwest = {version = "0.11.12", features = ["json"]}
tokio = {version = "1", features = ["full"]}
serde_json = "1.0.82"
        
        
    

Ahora modificamos nuestro archivo src/main.rs para poder obtener la respuesta en formato JSON y que podamos manipularla con la estructura Value.

        
        
            // src/main.rs

use serde_json::Value;
use reqwest::header;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configurando los headers necesarios
    let mut headers = header::HeaderMap::new();
    headers.insert("Accept-Encoding", header::HeaderValue::from_static("application/gzip"));
    // Reemplaza con tu propia key
    headers.insert("X-RapidAPI-Key", header::HeaderValue::from_static("<AGREGA ACA TU KEY>"));
    headers.insert("X-RapidAPI-Host", header::HeaderValue::from_static("google-translate1.p.rapidapi.com"));

    // Una buena idea si se van hacer muchos requests
    // es crear un "Cliente" el cual se puede reutilizar
    // para varias peticiones, ahora tambien agregamos los headers
    let cliente = reqwest::Client::builder()
        .default_headers(headers)
        .build()?;

    // Ahora las peticiones las podemos hacer con el ciente
    // en lugar de utilizar directamente el crate
    let url = "https://google-translate1.p.rapidapi.com/language/translate/v2/languages";

    let r = cliente.get(url)
            .send()
            .await?;
    
    // Ahora mostremos en pantalla la composicion completa de la respuesta.
    println!("Esta es la respuesta en crudo");
    println!("{:?}", r);

    // Mostremos el codigo de respuesta enviado por el servicio
    println!("Este es el status code retornado por el servicio");
    println!("{}", r.status().as_u16());

    let resp_json = r.json::<Value>().await?;

    println!("Respuesta mas clara en formato JSON");
    println!("{:?}", resp_json);

    Ok(())
}
        
        
    

Si ahora corremos nuestro programa, vamos a ver que después del status code, gracias a serde y serde_json, vemos un objeto representando la respuesta del API con todos los lenguajes soportados por el servicio.

Por tratarse de un objeto de "Value" puedes manipular la respuesta como la necesites, si no sabes como hacerlo no olvides leer nuestra miniserie sobre serde y serde_json empezando por acá 😊

Convertir un String en Formato JSON a un Objeto con Rust.

Recuerda también que si sabes y puedes representar la respuesta de un servicio en una estructura personalizada, puedes hacerlo y convertir directamente el valor de la respuesta a tu estructura simplemente reemplazando <Value> por <[Nombre de tu estructura]> en esta línea:

        
        
            
let resp_json = r.json::<Value>().await?; // Reemplaza Value por el nombre de tu estructura.
        
        
    

Ahora ya sabemos realizar peticiones GET hacia un API y procesar su respuesta 😎

Como se mencionaba anteriormente, las peticiones GET generalmente son para consultar datos o pedir a un servicio que nos devuelta el resultado de algún procesamiento, generalmente las peticiones GET no tienen un "cuerpo" (body), pero hay otro tipo de peticiones que pueden servir por ejemplo para crear algún registro, subir una imagen o actualizar algún dato, en estos casos GET no es lo mejor y se deben usar otros métodos HTTP como POST, PUT, PATCH, etc.

En el siguiente ejemplo vamos a aprender a hacer una petición de tipo POST creando un cuerpo o "body" que luego será procesado por el API.

Peticiones POST con Rust.

Ahora vamos a realizar peticiones POST, generalmente se utilizan este tipo de peticiones para guardar o procesar información que necesite un "Body" o cuerpo del request.

Este body puede ser de muchos tipos como por ejemplo:

  • JSON
  • Formularios
  • Binarios
  • URL

Vamos a hacer un ejemplo siempre con un endpoint de traducciones, pero diferente al de google translate, así vamos aprendiendo con más de un endpoint 🤠

El endpoint siempre se encuentra en RapidAPI, recuerda suscribirte si no lo has hecho, es gratis. Puedes ingresar a esta dirección Text Translator.

Al ingresar buscamos la sección de pricing y te suscribes a la versión Basic que es gratuita y nos permitirá hasta 100,000 request al mes (suficientes para aprender a utilizar APIs 🤩)

Nuevamente volvemos a la página de los endpoints y nos fijamos siempre en las instrucciones en el panel derecho, por motivos de seguridad he ocultado mi token, recuerda que para hacer los ejemplos debes reemplazar por tu propio token.

Es importante ver que ahora en el ejemplo en node existe una constante llamada encodedParams con los datos que necesita el API para hacer las traducciones en el cual necesitará un lenguaje de origina, un lenguaje destino o al cual queremos tener la traducción y un texto a traducir, pero también en los headers podemos ver:

content-type: application/x-www-form-urlencoded

el cual nos está diciendo que el request debemos enviar los datos como un formulario, hagamos nuestro ejemplo en Rust y veamos como enviar esos parámetros.

Para ello creemos un nuevo proyecto, en la carpeta de tu preferencia ejecuta el comando:

cargo new http-post-translate

y luego nos movemos a la carpeta

cd http-post-translate

Ahora vamos a configurar nuestro archivo Cargo.toml con los crates necesarios, vamos a incluir serde_json para el manejo de la respuesta que viene en formato json

        
        
            // Cargo.toml

[package]
name = "http-post-translate"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
reqwest = {version = "0.11.12", features = ["json"]}
tokio = {version = "1", features = ["full"]}
serde_json = "1.0.82"
        
        
    

Y ahora en nuestro src/main.rs vamos a agregar los encabezados necesarios, recuerda reemplazar el token de seguridad por tu propio token, también de una vez vamos a agregar los parámetros del formulario.

        
        
            // src/main.rs

use serde_json::Value;
use reqwest::header;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configurando los headers necesarios
    let mut headers = header::HeaderMap::new();
    headers.insert("Accept-Encoding", header::HeaderValue::from_static("application/x-www-form-urlencoded"));
    // Reemplaza con tu propia key
    headers.insert("X-RapidAPI-Key", header::HeaderValue::from_static("<AGREGA ACA TU KEY>"));
    headers.insert("X-RapidAPI-Host", header::HeaderValue::from_static("text-translator2.p.rapidapi.com"));

    // Creando el body para el envio de un FORM
    let params = [
        ("source_language", "en"),
        ("target_language", "es"),
        ("text", "Hello World!")
    ];

    // Una buena idea si se van hacer muchos requests
    // es crear un "Cliente" el cual se puede reutilizar
    // para varias peticiones, ahora tambien agregamos los headers
    let cliente = reqwest::Client::builder()
        .default_headers(headers)
        .build()?;

    // Ahora las peticiones las podemos hacer con el ciente
    // en lugar de utilizar directamente el crate
    let url = "https://text-translator2.p.rapidapi.com/translate";

    let r = cliente.post(url)
            .form(&params)
            .send()
            .await?;
    
    // Mostremos el codigo de respuesta enviado por el servicio
    println!("Este es el status code retornado por el servicio");
    println!("{}", r.status().as_u16());

    let resp_json = r.json::<Value>().await?;

    println!("Respuesta en formato JSON");
    println!("{:?}", resp_json);

    Ok(())
}

        
        
    

Si ejecutamos el programa con

cargo run

veremos, si todo está bien, que nos muestra el siguiente mensaje de respuesta:

        
        
            
Este es el status code retornado por el servicio
200
Respuesta en formato JSON
Object {"data": Object {"translatedText": String("Hola Mundo!")}, "status": String("success")}
        
        
    

Vemos como ha traducido "Hello World!" por "Hola Mundo!"

Los parámetros es tan fácil agregarlo como una lista de tuplas, en el ejemplo estamos solicitando al API que traduzca "Hello World!" como fuente en inglés, al español:

        
        
            
let params = [
        ("source_language", "en"),
        ("target_language", "es"),
        ("text", "Hello World!")
];
        
        
    

Y ahora en el send, de forma explícita le hacemos saber a el API que enviaremos estos parámetros como form.

        
        
            
let r = cliente.post(url)
            .form(&params)
            .send()
            .await?;
        
        
    

Si la petición fuera en formato json, solamente tendríamos que reemplazar el .form por un .json, solamente considerando que enviar json require tener una estructura o Hashmap como en este ejemplo.

Conclusiones.

En este artículo, hemos intentado aprender sore como hacer peticiones HTTP y API, el tema es bastante grande, sin embargo, el objetivo ha sido tener las bases para poder iniciar a trabajar con API en Rust.

Acá nos hemos concentrado únicamente del lado del "cliente", pero no hemos creado un API, si te gustaría aprender a crear un API con Rust, déjalo en la caja de comentarios y con mucho gusto trabajaré el artículo 😇

Recuerda que puedes encontrar los códigos de ejemplo de esta publicación en el repositorio en github dando click acá.

Si esta publicación te ha gustado o te ha sido de utilidad, compártela con tus amigos y en tus redes sociales.

Escribe en la caja de comentarios lo que te ha gustado (o no) de esta publicación y de lo que te gustaría que se publicara.

println!("Hasta la próxima!");


 Utilizamos cookies propias y de terceros para mejorar tu experiencia, mostrar publicidad y análisis de navegación, puedes encontrar el detalle en nuestra Política de Cookies