Visão geral
Neste tutorial, você pode aprender a criar um aplicação web Rust usando a estrutura web Rocket. O driver Rust permite que você aproveite recursos como gerenciamento de memória, tempo de vida e pooling de banco de dados para melhorar o desempenho do seu aplicativo.
Depois de concluir este tutorial, você terá um aplicação da web com rotas para executar operações CRUD.
Dica
Aplicativo completo
Para visualizar uma versão completa do aplicativo criado neste tutorial, visite o repositório mongodb-api-rs no GitHub.
Pré-requisitos
Verifique se você tem o Rust 1.71.1 ou posterior e o Cargo, o gerenciador de pacote do Rust, instalado em seu ambiente de desenvolvimento.
Para obter informações sobre como instalar o Rust and Cargo, consulte o guia oficial do Rust sobre como baixar e instalar o Rust.
Você também deve configurar um cluster MongoDB Atlas . Para saber como criar um cluster, consulte a etapa Criar uma implementação do MongoDB do guia Início rápido. Salve sua string de conexão em um local seguro para usar mais tarde no tutorial.
Passos
Selecione o tempo de execução assíncrono.
Ao usar o driver Rust, você deve selecionar o tempo de execução síncrono ou assíncrono. Este tutorial usa o tempo de execução assíncrono, que é mais adequado para criar APIs.
O driver é executado com o tempo de execução assíncrono tokio
por padrão.
Para saber mais sobre os tempos de execução disponíveis, consulte o guia de APIs assíncronas e síncronas.
Insira dados de amostra.
Selecione o INSERT DOCUMENT botão e cole o conteúdo do arquivo Bread_data.json no repositório do aplicativo de amostra.
Após inserir os dados, você pode visualizar os documentos de amostra na coleção recipes
.
Instale o Foguete.
Abra seu IDE e insira seu diretório de projeto . Execute o seguinte comando a partir da raiz do seu projeto para instalar a estrutura web doRocket:
cargo add -F json rocket
Verifique se a lista de dependências em seu arquivo Cargo.toml
contém uma entrada para rocket
.
Você também deve adicionar uma crate desenvolvida pela Spark que permite usar um wrapper para gerenciar um pool de coleta para as conexões assíncronas feitas pelo cliente MongoDB . Esta crate permite parametrizar seus bancos de dados e coleções MongoDB e fazer com que cada função de aplicativo receba sua própria conexão para uso.
Execute o seguinte comando para adicionar a crate rocket_db_pools
:
cargo add -F mongodb rocket_db_pools
Verifique se a lista de dependências em seu arquivo Cargo.toml
contém uma entrada para rocket_db_pools
que contém um sinalizador de recurso para mongodb
.
Configure o Foguete.
Para configurar o Foguete para usar o banco de dados do bread
, crie um arquivo chamado Rocket.toml
na raiz do seu projeto . O Foguete procura este arquivo para ler as definições de configuração. Você também pode armazenar sua string de conexão do MongoDB neste arquivo.
Cole a seguinte configuração em Rocket.toml
:
[default.databases.db] url = "<connection string>"
Para saber mais sobre como configurar oRocket, consulte Configuração na documentação doRocket.
Saiba mais sobre a estrutura do aplicativo.
Antes de começar a escrever a API, saiba mais sobre a estrutura de um aplicativo Foguete simples e crie os arquivos correspondentes em seu aplicação.
O diagrama a seguir demonstra a estrutura de arquivos que seu aplicativo Foguete deve ter e explica a função de cada arquivo:
. ├── 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
Crie o diretório src
e os arquivos que ele contém, de acordo com o diagrama anterior. Neste ponto, os arquivos podem estar vazios.
Configure a conexão do banco de dados .
Cole o seguinte código no arquivo db.rs
.
use rocket_db_pools::{mongodb::Client, Database}; pub struct MainDatabase(Client);
Você também deve anexar a estrutura do banco de dados à sua instância do Foguete. No main.rs
, inicialize o banco de dados e anexe-o, como mostrado no seguinte código:
mod db; mod models; mod routes; use rocket::{launch, routes}; use rocket_db_pools::Database; fn rocket() -> _ { rocket::build() .attach(db::MainDatabase::init()) .mount() }
Seu IDE pode gerar um erro informando que mount()
está sem argumentos. Você pode ignorar este erro, pois adicionará rotas em uma etapa posterior.
Criar modelos de dados.
Definir estruturas consistentes e úteis para representar seus dados é importante para manter a segurança do tipo e reduzir erros de tempo de execução.
Em seu arquivo models.rs
, defina uma estrutura Recipe
que represente uma receita para assar pão.
use mongodb::bson::oid::ObjectId; use rocket::serde::{Deserialize, Serialize}; pub struct Recipe { pub id: Option<ObjectId>, pub title: String, pub ingredients: Vec<String>, pub temperature: u32, pub bake_time: u32, }
Configure rotas de API.
O roteamento permite que o programa direcione a solicitação para o endpoint apropriado para enviar ou receber os dados. O arquivo routes.rs
armazena todas as rotas definidas na API.
Adicione o seguinte código ao seu arquivo routes.rs
para definir a rota do índice e uma rota get_recipes()
simples:
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}; pub fn index() -> Json<Value> { Json(json!({"status": "It is time to make some bread!"})) } 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 escrever as rotas restantes, adicione as rotas à principal função de lançamento do Foguete.
No main.rs
, substitua os argumentos para mount()
para que o arquivo se assemelhe ao seguinte código:
mod db; mod models; mod routes; use rocket::{launch, routes}; use rocket_db_pools::Database; 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 ], ) }
Implemente o tratamento e respostas a erros.
Em seu aplicativo, você deve implementar o tratamento de erros e respostas personalizadas para lidar com resultados inesperados de suas operações CRUD.
Instale a crate serde_json
executando o seguinte comando:
cargo add serde_json
Esta crate inclui o enumeração Value
que representa um valor JSON.
Você pode especificar que suas rotas retornem códigos de status HTTP usando as estruturas status::Custom
do Vector, que permitem especificar o código de status HTTP e quaisquer dados personalizados a serem retornados. A etapa a seguir descreve como escrever rotas que retornam o tipo status::Custom
.
Escreva rotas de operação CRUD.
criar
Quando você tenta criar dados no MongoDB, há duas saídas possíveis:
O documento foi criado com sucesso, portanto, seu aplicativo retorna
HTTP 201
.Ocorreu um erro durante a inserção, portanto seu aplicativo retorna
HTTP 400
.
Adicione a seguinte rota ao seu arquivo routes.rs
para definir a rota do create_recipe()
e implementar o tratamento de erros:
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"})), ) }
Leia
Quando você tenta ler dados do MongoDB, há duas saídas possíveis:
Retorne o vetor de documentos correspondentes.
Retornar um vetor vazio, porque não há documentos correspondentes ou porque ocorreu um erro.
Devido a esses resultados esperados, substitua sua rota get_recipes()
pelo seguinte código:
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![]); }
Outras operações
Você pode copiar as get_recipe()
update_recipe()
rotas, e delete_recipe()
do arquivo concerns.rs no repositório de aplicativo de amostra.
Rotas de teste para realizar operações CRUD.
Inicie seu aplicação executando o seguinte comando em seu terminal:
cargo run
Em outra janela do terminal, execute o seguinte comando para testar a rota 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"}
Execute o seguinte comando para testar a rota 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},...]
Execute o seguinte comando para testar a rota delete_recipe()
. Substitua o espaço reservado <id>
por um valor _id
conhecido da sua coleção, que pode se assemelhar 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"}
Conclusão
Neste tutorial, você aprendera como construir um aplicação web simples com oRocket para executar operações CRUD.
Recursos
Para saber mais sobre as operações CRUD, consulte os seguintes guias: