Docs Menu
Docs Home
/ /

Aplicación web CRUD con Rocket

En este tutorial, puede aprender a crear una aplicación web Rust utilizando el Rocket Marco web. El controlador Rust permite aprovechar funciones como la gestión de memoria, los tiempos de vida y la agrupación de bases de datos para mejorar el rendimiento de la aplicación.

Después de completar este tutorial, tendrá una aplicación web con rutas para realizar operaciones CRUD.

Tip

Aplicación completa

Para ver una versión completa de la aplicación creada en este tutorial, visite el repositorio mongodb-api-rs en GitHub.

Asegúrese de tener Rust 1.74 o posterior, y Cargo, el administrador de paquetes de Rust, instalado en su entorno de desarrollo.

Para obtener información sobre cómo instalar Rust y Cargo, consulta la guía oficial de Rust en descargar e instalar Rust.

También debe configurar un clúster de MongoDB Atlas. Para saber cómo crear un clúster, consulte Paso "Crear una implementación de MongoDB" de la guía de inicio rápido. Guarde la cadena de conexión en un lugar seguro para usarla más adelante en el tutorial.

1

Ejecute los siguientes comandos para crear e ingresar el rust-crud-app directorio del proyecto:

cargo new rust-crud-app
cd rust-crud-app
2

Ejecute el siguiente comando para agregar el controlador Rust:

cargo add mongodb

Verifique que su archivo Cargo.toml contenga la siguiente entrada para el controlador Rust:

[dependencies]
mongodb = "3.5.1"
3

Al usar el controlador de Rust, debe seleccionar el entorno de ejecución síncrono o asincrónico. Este tutorial utiliza el entorno de ejecución asíncrono, que es más adecuado para crear API.

El controlador se ejecuta con el tiempo de ejecución asíncrono tokio de manera predeterminada.

Para obtener más información sobre los tiempos de ejecución disponibles, consulte la guía de API asincrónicas y sincrónicas.

4

Acceda a la interfaz de usuario de Atlas y luego seleccione el icono Collections Pestaña en la configuración del clúster. Seleccione el botón + Create Database. En la ventana modal, cree una base de datos llamada bread y, dentro de ella, una colección llamada recipes.

5

Selecciona el botón INSERT DOCUMENT y pega el contenido del archivo bread_data.json en el repositorio de la aplicación de muestra.

Después de insertar los datos, podrá ver los documentos de muestra en la colección recipes.

6

Abra su IDE e ingrese al directorio de su proyecto. Ejecute el siguiente comando desde la raíz del proyecto para instalar Rocket Web Framework:

cargo add -F json rocket

Verifique que la lista de dependencias en su archivo Cargo.toml contenga una entrada para rocket.

También debe agregar un contenedor desarrollado por Rocket que le permite usar un contenedor para administrar un grupo de colecciones para las conexiones asíncronas realizadas por su cliente MongoDB. Este contenedor le permite parametrizar sus bases de datos y colecciones MongoDB y que cada función de la aplicación reciba su propia conexión.

Ejecute el siguiente comando para agregar la caja rocket_db_pools:

cargo add -F mongodb rocket_db_pools

Verifique que la lista de dependencias en su archivo Cargo.toml contenga una entrada para rocket_db_pools que contenga un indicador de característica para mongodb.

7

Para configurar Rocket y usar la base de datos bread, crea un archivo llamado Rocket.toml en la raíz de tu proyecto. Rocket busca este archivo para leer la configuración. También puedes almacenar tu cadena de conexión de MongoDB en este archivo.

Pegue la siguiente configuración en Rocket.toml:

[default.databases.db]
url = "<connection string>"

Para obtener más información sobre la configuración de Rocket, consulta Configuración en la documentación de Rocket.

8

Antes de comenzar a escribir la API, aprenda sobre la estructura de una aplicación Rocket simple y cree los archivos correspondientes en su aplicación.

El siguiente diagrama demuestra la estructura de archivos que debe tener tu aplicación Rocket y explica la función de cada archivo:

.
├── Cargo.lock # Dependency info
├── Cargo.toml # Project and dependency info
├── Rocket.toml # Rocket configuration
└── src # Directory for all app code
├── db.rs # Establishes database connection
├── main.rs # Starts the web app
├── models.rs # Organizes data
└── routes.rs # Stores API routes

Cree el directorio src y los archivos que contiene, según el diagrama anterior. En este punto, los archivos pueden estar vacíos.

9

Pegue el siguiente código en el archivo db.rs.

use rocket_db_pools::{mongodb::Client, Database};
#[derive(Database)]
#[database("db")]
pub struct MainDatabase(Client);

También debe adjuntar la estructura de la base de datos a su instancia de Rocket. En main.rs, inicialice la base de datos y adjúntela, como se muestra en el siguiente código:

mod db;
mod models;
mod routes;
use rocket::{launch, routes};
use rocket_db_pools::Database;
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(db::MainDatabase::init())
.mount()
}

Tu IDE podría generar un error indicando que a mount() le faltan argumentos. Puedes ignorar este error, ya que agregarás rutas más adelante.

10

Definir estructuras consistentes y útiles para representar sus datos es importante para mantener la seguridad de tipos y reducir errores de tiempo de ejecución.

En su archivo models.rs, defina una estructura Recipe que represente una receta para hornear pan.

use mongodb::bson::oid::ObjectId;
use rocket::serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Recipe {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
pub title: String,
pub ingredients: Vec<String>,
pub temperature: u32,
pub bake_time: u32,
}
11

El enrutamiento permite que el programa dirija la solicitud al punto final adecuado para enviar o recibir los datos. El archivo routes.rs almacena todas las rutas definidas en la API.

Agregue el siguiente código a su archivo routes.rs para definir la ruta de índice y una ruta simple get_recipes():

use crate::db::MainDatabase;
use crate::models::Recipe;
use mongodb::bson::doc;
use rocket::{futures::TryStreamExt, get, serde::json::Json};
use rocket_db_pools::{mongodb::Cursor, Connection};
#[get("/")]
pub fn index() -> Json<Value> {
Json(json!({"status": "It is time to make some bread!"}))
}
#[get("/recipes", format = "json")]
pub async fn get_recipes(db: Connection<MainDatabase>) -> Json<Vec<Recipe>> {
let recipes: Cursor<Recipe> = db
.database("bread")
.collection("recipes")
.find(None, None)
.await
.expect("Failed to retrieve recipes");
Json(recipes.try_collect().await.unwrap())
}

Antes de escribir las rutas restantes, agregue las rutas a la función de lanzamiento principal de Rocket.

En main.rs, reemplace los argumentos de mount() para que el archivo se parezca al siguiente código:

mod db;
mod models;
mod routes;
use rocket::{launch, routes};
use rocket_db_pools::Database;
#[launch]
fn rocket() -> _ {
rocket::build().attach(db::MainDatabase::init()).mount(
"/",
routes![
routes::index,
routes::get_recipes,
routes::create_recipe,
routes::update_recipe,
routes::delete_recipe,
routes::get_recipe
],
)
}
12

En su aplicación, debe implementar el manejo de errores y respuestas personalizadas para lidiar con resultados inesperados de sus operaciones CRUD.

Instale el paquete serde_json ejecutando el siguiente comando:

cargo add serde_json

Esta caja incluye la enumeración Value que representa un valor JSON.

Puedes especificar que tus rutas devuelvan códigos de estado HTTP mediante las estructuras status::Custom de Rocket, que te permiten especificar el código de estado HTTP y cualquier dato personalizado que se devuelva. El siguiente paso describe cómo escribir rutas que devuelvan el tipo status::Custom.

13

Cuando intenta crear datos en MongoDB, hay dos resultados posibles:

  • El documento se creó correctamente, por lo que su aplicación devuelve HTTP 201.

  • Se produjo un error durante la inserción, por lo que su aplicación devuelve HTTP 400.

Agregue la siguiente ruta a su archivo routes.rs para definir la ruta create_recipe() e implementar el manejo de errores:

#[post("/recipes", data = "<data>", format = "json")]
pub async fn create_recipe(
db: Connection<MainDatabase>,
data: Json<Recipe>,
) -> status::Custom<Json<Value>> {
if let Ok(res) = db
.database("bread")
.collection::<Recipe>("recipes")
.insert_one(data.into_inner(), None)
.await
{
if let Some(id) = res.inserted_id.as_object_id() {
return status::Custom(
Status::Created,
Json(json!({"status": "success", "message": format!("Recipe ({}) created successfully", id.to_string())})),
);
}
}
status::Custom(
Status::BadRequest,
Json(json!({"status": "error", "message":"Recipe could not be created"})),
)
}

Cuando intenta leer datos de MongoDB, hay dos resultados posibles:

  • Devuelve el vector de documentos coincidentes.

  • Devuelve un vector vacío, porque no hay documentos coincidentes o porque ocurrió un error.

Debido a estos resultados esperados, reemplace su ruta get_recipes() con el siguiente código:

#[get("/recipes", format = "json")]
pub async fn get_recipes(db: Connection<MainDatabase>) -> Json<Vec<Recipe>> {
let recipes = db
.database("bread")
.collection("recipes")
.find(None, None)
.await;
if let Ok(r) = recipes {
if let Ok(collected) = r.try_collect::<Vec<Recipe>>().await {
return Json(collected);
}
}
return Json(vec![]);
}

Puede copiar las get_recipe() update_recipe() delete_recipe() rutas, y del archivo routes.rs en el repositorio de aplicaciones de muestra.

14

Inicie su aplicación ejecutando el siguiente comando en su terminal:

cargo run

En otra ventana de terminal, ejecute el siguiente comando para probar la ruta create_recipe():

curl -v --header "Content-Type: application/json" --request POST --data '{"title":"simple bread recipe","ingredients":["water, flour"], "temperature": 250, "bake_time": 120}' http://127.0.0.1:8000/recipes
{"status":"success","message":"Recipe (684c4245f5a3ca09efa92593) created successfully"}

Ejecute el siguiente comando para probar la ruta get_recipes():

curl -v --header "Content-Type: application/json" --header "Accept: application/json" http://127.0.0.1:8000/recipes/
[{"_id":...,"title":"artisan","ingredients":["salt","flour","water","yeast"],"temperature":404,"bake_time":5},
{"_id":...,"title":"rye","ingredients":["salt"],"temperature":481,"bake_time":28},...]

Ejecute el siguiente comando para probar la ruta delete_recipe(). Reemplace el marcador <id> con un valor _id conocido de su colección, que podría ser similar a 68484d020f561e78c03c7800:

curl -v --header "Content-Type: application/json" --header "Accept: application/json" --request DELETE http://127.0.0.1:8000/recipes/<id>
{"status":"","message":"Recipe (68484d020f561e78c03c7800) successfully deleted"}

En este tutorial, aprendió a crear una aplicación web simple con Rocket para realizar operaciones CRUD.

Para obtener más información sobre las operaciones CRUD, consulte las siguientes guías:

Volver

Operaciones compuestas