Overview
在本教程中,您可以学习;了解如何使用 Rocket Web框架创建Rust Web应用程序。 Rust驾驶员允许您利用内存管理、生命周期和数据库池化等功能来提高应用程序的性能。
完成本教程后,您将拥有一个 Web应用程序,其中包含用于执行增删改查操作的路由。
先决条件
确保您的开发环境中安装了Rust 1.71.1 或更高版本,以及Rust包管理器 Cargo。
有关如何安装Rust和 Cargo 的信息,请参阅有关 下载和安装Rust 的Rust官方指南。
您还必须设立一个MongoDB Atlas 群集。要学习;了解如何创建集群,请参阅快速入门指南的 创建MongoDB部署步骤。将连接字符串保存在安全位置,以便稍后在本教程中使用。
步骤
选择异步运行时。
使用Rust驾驶员时,必须选择同步或异步运行时。本教程使用异步运行时,更适合构建 API。
默认下,驾驶员使用异步tokio
运行时运行。
要学习;了解有关可用运行时的更多信息,请参阅 异步和同步 API指南。
插入示例数据。
选择INSERT DOCUMENT 按钮,然后将 bread_data.json文件的内容粘贴到示例应用存储库中。
插入数据后,您可以在 recipes
集合中查看示例文档。
安装Rocket。
打开 IDE 并进入项目目录。从项目根目录运行以下命令以安装 Rocket Web框架:
cargo add -F json rocket
验证 Cargo.toml
文件中的依赖项列表是否包含 rocket
的条目。
您还必须添加一个由 Rocket 开发的包,允许您使用包装器来管理MongoDB客户端建立的异步连接的集合池。此包允许您参数化MongoDB数据库和集合,并让每个应用函数接收自己的连接以供使用。
运行以下命令以添加 rocket_db_pools
包:
cargo add -F mongodb rocket_db_pools
验证 Cargo.toml
文件中的依赖项列表是否包含 rocket_db_pools
的条目,而该条目包含 mongodb
的功能标志。
了解应用结构。
在开始编写API之前,学习;了解简单的 Rocket应用的结构,并在应用程序中创建相应的文件。
下图展示了您的Rocket应用必须具有的文件结构,并解释了每个文件的功能:
. ├── 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
根据上图,创建 src
目录及其包含的文件。点,文件可以为空。
设置数据库连接。
将以下代码粘贴到 db.rs
文件中。
use rocket_db_pools::{mongodb::Client, Database}; pub struct MainDatabase(Client);
您还必须将数据库结构附加到您的Rocket实例。在 main.rs
中,初始化数据库并将其附加,如以下代码所示:
mod db; mod models; mod routes; use rocket::{launch, routes}; use rocket_db_pools::Database; fn rocket() -> _ { rocket::build() .attach(db::MainDatabase::init()) .mount() }
您的 IDE 可能会引发错误,说明 mount()
缺少参数。您可以忽略此错误,因为您将在后续步骤中添加路由。
创建数据模型。
定义一致且有用的结构体来表示数据对于维护类型安全和减少运行时错误非常重要。
在您的 models.rs
文件中,定义一个 Recipe
结构体来表示烘焙面包的配方。
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, }
设置API路由。
路由允许程序将请求定向到适当的端点以发送或接收数据。文件routes.rs
存储API中定义的所有路由。
将以下代码添加到 routes.rs
文件中,以定义索引路由和简单的 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}; 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()) }
在写入其余路由之前,请将路由添加到Rocket 的主启动函数中。
在 main.rs
中,将参数替换为 mount()
,使文件类似于以下代码:
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 ], ) }
写入增删改查操作路由。
创建
当您尝试在MongoDB中创建数据时,有两种可能的结果:
文档已成功创建,因此您的应用会返回
HTTP 201
。插入期间发生错误,因此您的应用会返回
HTTP 400
。
将以下路由添加到 routes.rs
文件以定义 create_recipe()
路由并实现错误处理:
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"})), ) }
读取
当您尝试从MongoDB读取数据时,有两种可能的结果:
返回匹配文档的向量。
返回一个空向量,因为没有匹配的文档或发生错误。
由于这些预期结果,请将 get_recipes()
路由替换为以下代码:
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![]); }
其他操作
您可以从示例应用存储库的routes.rs文件中复制 get_recipe()
、update_recipe()
和delete_recipe()
路由。
测试执行增删改查操作的路由。
通过在终端中运行以下命令来启动应用程序:
cargo run
在另一个终端窗口中,运行以下命令以测试 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"}
运行以下命令以测试 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},...]
运行以下命令以测试 delete_recipe()
路由。将 <id>
占位符替换为集合中已知的 _id
值,该值可能类似于 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"}
结论
在本教程中,您学习了如何使用 Rocket构建简单的 Web应用程序来执行增删改查操作。
资源
要学习;了解有关增删改查操作的更多信息,请参阅以下指南: