Docs 菜单
Docs 主页
/ / /
Rust 驱动程序
/ /

教程:使用 Rocket 创建增删改查 Web 应用程序

在本教程中,您可以学习;了解如何使用 Rocket Web框架创建Rust Web应用程序。 Rust驾驶员允许您利用内存管理、生命周期和数据库池化等功能来提高应用程序的性能。

完成本教程后,您将拥有一个 Web应用程序,其中包含用于执行增删改查操作的路由。

提示

完整应用程序

要查看本教程中创建的应用的完整版本,请访问 GitHub 上的 mongodb-api-rs存储库。

确保您的开发环境中安装了Rust 1.71.1 或更高版本,以及Rust包管理器 Cargo。

有关如何安装Rust和 Cargo 的信息,请参阅有关 下载和安装Rust 的Rust官方指南。

您还必须设立一个MongoDB Atlas 群集。要学习;了解如何创建集群,请参阅快速入门指南的 创建MongoDB部署步骤。将连接字符串保存在安全位置,以便稍后在本教程中使用。

1

运行以下命令,创建并进入 rust-crud-app项目目录:

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

运行以下命令以添加Rust驾驶员:

cargo add mongodb

验证您的 Cargo.toml文件是否包含Rust驾驶员的以下条目:

[dependencies]
mongodb = "3.2.3"
3

使用Rust驾驶员时,必须选择同步或异步运行时。本教程使用异步运行时,更适合构建 API。

默认下,驾驶员使用异步tokio 运行时运行。

要学习;了解有关可用运行时的更多信息,请参阅 异步和同步 API指南。

4

访问Atlas用户界面,然后在集群设置中选择 Collections标签页。选择 + Create Database 按钮。在模态中,创建一个名为 bread 的数据库,并在其中创建一个名为 recipes 的集合。

5

选择INSERT DOCUMENT 按钮,然后将 bread_data.json文件的内容粘贴到示例应用存储库中。

插入数据后,您可以在 recipes集合中查看示例文档。

6

打开 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 的功能标志。

7

要将Rocket 配置为使用 bread数据库,请在项目根目录中创建一个名为 Rocket.toml 的文件。 Rocket 会查找此文件以读取配置设置。您还可以将MongoDB连接字符串存储在此文件中。

将以下配置粘贴到 Rocket.toml 中:

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

要学习;了解有关配置Rocket的更多信息,请参阅Rocket文档中的配置。

8

在开始编写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目录及其包含的文件。点,文件可以为空。

9

将以下代码粘贴到 db.rs 文件中。

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

您还必须将数据库结构附加到您的Rocket实例。在 main.rs 中,初始化数据库并将其附加,如以下代码所示:

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()
}

您的 IDE 可能会引发错误,说明 mount() 缺少参数。您可以忽略此错误,因为您将在后续步骤中添加路由。

10

定义一致且有用的结构体来表示数据对于维护类型安全和减少运行时错误非常重要。

在您的 models.rs文件中,定义一个 Recipe 结构体来表示烘焙面包的配方。

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

路由允许程序将请求定向到适当的端点以发送或接收数据。文件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};
#[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())
}

在写入其余路由之前,请将路由添加到Rocket 的主启动函数中。

main.rs 中,将参数替换为 mount(),使文件类似于以下代码:

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

在您的应用中,您必须实现错误处理和自定义响应,以处理增删改查操作的意外结果。

通过运行以下命令来安装 serde_json 包:

cargo add serde_json

此 crate 包含表示JSON值的 Value枚举。

您可以使用 Rocket 的 status::Custom 结构指定路由返回HTTP状态代码,该结构允许您指定HTTP状态代码和要返回的任何自定义数据。以下步骤描述如何写入返回 status::Custom 类型的路由。

13

当您尝试在MongoDB中创建数据时,有两种可能的结果:

  • 文档已成功创建,因此您的应用会返回 HTTP 201

  • 插入期间发生错误,因此您的应用会返回 HTTP 400

将以下路由添加到 routes.rs文件以定义 create_recipe() 路由并实现错误处理:

#[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"})),
)
}

当您尝试从MongoDB读取数据时,有两种可能的结果:

  • 返回匹配文档的向量。

  • 返回一个空向量,因为没有匹配的文档或发生错误。

由于这些预期结果,请将 get_recipes() 路由替换为以下代码:

#[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![]);
}

您可以从示例应用存储库的routes.rs文件中复制 get_recipe()update_recipe()delete_recipe() 路由。

14

通过在终端中运行以下命令来启动应用程序:

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应用程序来执行增删改查操作。

要学习;了解有关增删改查操作的更多信息,请参阅以下指南:

后退

复合运算符

在此页面上