[dependencies]
actix-web = "4"
serde = "1.0.136"
dotenv = "0.15.0"
futures = "0.3"

[dependencies.mongodb]
version = "2.2.0"
default-features = false
features = ["async-std-runtime"]

actix-web = "4"是一個基于 Rust 的框架,用于構建 Web 應用程序。

serde = "1.0.136"是一個用于序列化和反序列化 Rust 數據結構的框架。例如,將 Rust 結構轉換為 JSON。

dotenv = "0.15.0"是一個用于管理環境變量的庫。

futures = "0.3"是一個用 Rust 進行異步編程的庫

[dependencies.mongodb]是用于連接 MongoDB 的驅動程序。它還指定所需的版本和功能類型(異步 API)。

我們需要運行以下命令來安裝依賴項:

 cargo build

應用程序入口點

安裝項目依賴項之后,請修改位于src文件夾中的main.rs文件,將其內容更新為以下所示:

use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().json("Hello from rust and mongoDB")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(hello))
.bind(("localhost", 8080))?
.run()
.await
}

上面的代碼片段執行以下操作:

接下來,我們可以通過在終端中運行以下命令來測試我們的應用程序。

cargo run
測試應用程序

Rust 中的模塊系統

在Rust中,模塊是一種機制,用于將代碼拆分為可重用的組件,并管理這些組件之間的可見性。模塊有助于我們維護項目,使其具有良好的結構。

為此,我們需要導航到該src文件夾??并創建apimodelsrepository文件夾以及相應的mod.rs 文件來管理可見性。

更新了項目文件夾結構

api用于模塊化 API 處理程序。

models用于模塊化數據邏輯。

repository用于模塊化數據庫邏輯。

添加對模塊的引用
要使用模塊中的代碼,我們需要將它們聲明為模塊并將它們導入到main.rs文件中。

//add the modules
mod api;
mod models;
mod repository;

use actix_web::{get, App, HttpResponse, HttpServer, Responder};

// the remaining part of our code goes here

設置 MongoDB

完成后,我們需要登錄或注冊我們的MongoDB帳戶。單擊項目下拉菜單,然后單擊“新建項目”按鈕。

新項目

輸入rust-api為項目名稱,單擊Next,然后單擊Create Project..

輸入項目名稱
創建項目

單擊構建數據庫

選擇共享作為數據庫類型。

共享內容以紅色突出顯示

單擊“創建”以設置集群。這可能需要一些時間來設置。

創建集群

接下來,我們需要創建一個用戶來從外部訪問數據庫,輸入用戶名密碼,然后單擊創建用戶。我們還需要點擊“添加我當前的IP地址”按鈕,以便添加IP地址從而安全地連接到數據庫。之后,點擊“完成并關閉”以保存所做的更改。

創建用戶
添加IP

保存更改后,我們應該看到數據庫部署屏幕,如下所示:

數據庫屏幕

將我們的應用程序連接到 MongoDB

完成配置后,我們需要將應用程序與創建的數據庫連接起來。為此,請單擊“連接”按鈕

連接到數據庫

單擊“連接您的應用程序”,將“驅動程序”更改為“版本”,如下所示。然后單擊復制圖標復制連接字符串。

連接應用程序
復制連接字符串

設置環境變量
接下來,我們需要使用之前創建的用戶密碼來修改復制的連接字符串,并同時更改數據庫的名稱。為了完成這個任務,首先,我們需要在項目的根目錄下創建一個名為.env的文件,并將復制的代碼片段粘貼到這個文件中。

MONGOURI=mongodb+srv://<YOUR USERNAME HERE>:<YOUR PASSWORD HERE>@cluster0.e5akf.mongodb.net/myFirstDatabese?retryWrites=true&w=majority

下面是正確填充的連接字符串的示例:

MONGOURI=mongodb+srv://malomz:malomzPassword@cluster0.e5akf.mongodb.net/golangDB?retryWrites=true&w=majority

創建 REST API

設置完成后,我們需要創建一個模型來表示我們的應用程序數據。為此,我們需要導航到該models文件夾??,并在此文件夾中創建一個user_model.rs文件并添加以下代碼片段:

use mongodb::bson::oid::ObjectId;
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct User {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
pub name: String,
pub location: String,
pub title: String,
}

上面的代碼片段執行以下操作:

PS修飾符pub使結構及其屬性公開,并且可以從其他文件/模塊訪問。

接下來,我們需要將user_model.rs文件注冊為models模塊的一部分。為此,請打開位于models文件夾中的mod.rs文件,并添加以下代碼片段:

pub mod user_model;

創建用戶端點
模型完全設置并可供使用后,我們現在可以創建數據庫邏輯來創建用戶。為此,首先,我們需要導航到該repository文件夾??,并在此文件夾中創建一個mongodb_repo.rs文件并添加以下代碼片段:

use std::env;
extern crate dotenv;
use dotenv::dotenv;

use mongodb::{
bson::{extjson::de::Error},
results::{ InsertOneResult},
Client, Collection,
};
use crate::models::user_model::User;

pub struct MongoRepo {
col: Collection<User>,
}

impl MongoRepo {
pub async fn init() -> Self {
dotenv().ok();
let uri = match env::var("MONGOURI") {
Ok(v) => v.to_string(),
Err(_) => format!("Error loading env variable"),
};
let client = Client::with_uri_str(uri).unwrap();
let db = client.database("rustDB");
let col: Collection<User> = db.collection("User");
MongoRepo { col }
}

pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
let new_doc = User {
id: None,
name: new_user.name,
location: new_user.location,
title: new_user.title,
};
let user = self
.col
.insert_one(new_doc, None)
.await
.ok()
.expect("Error creating user");
Ok(user)
}
}

上面的代碼片段執行以下操作:

接下來,我們必須將該mongodb_repo.rs文件注冊為repository模塊的一部分。為了完成這個任務,請打開位于repository文件夾中的mod.rs文件,并向其中添加以下代碼片段:

pub mod mongodb_repos;

其次,我們需要創建一個處理程序,使用 create_user中的方法來創建用戶。為此,我們需要導航到該api文件夾??,并在此文件夾中創建一個user_api.rs文件并添加以下代碼片段:

use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
use actix_web::{
post,
web::{Data, Json},
HttpResponse,
};

#[post("/user")]
pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
let data = User {
id: None,
name: new_user.name.to_owned(),
location: new_user.location.to_owned(),
title: new_user.title.to_owned(),
};
let user_detail = db.create_user(data).await;
match user_detail {
Ok(user) => HttpResponse::Ok().json(user),
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}

上面的代碼片段執行以下操作:

PS: 定義參數時使用的 DataJson和結構分別用于管理跨路由共享的應用程序狀態和從請求負載中提取 JSON 數據。

最后,我們需要修改應用程序入口點以包含create_user處理程序。為此,我們需要導航到該main.rs文件并對其進行修改,如下所示:

mod api;
mod models;
mod repository;

//modify imports below
use actix_web::{web::Data, App, HttpServer};
use api::user_api::{create_user};
use repository::mongodb_repo::MongoRepo;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db = MongoRepo::init().await;
let db_data = Data::new(db);
HttpServer::new(move || {
App::new()
.app_data(db_data.clone())
.service(create_user)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

上面的代碼片段執行以下操作:

PS:關鍵字move用于閉包中,它的作用是賦予閉包對MongoDB配置的所有權。

獲取用戶端點
要獲取用戶的詳細信息,我們必須首先通過get_user向實現塊mongodb_repo.rs添加方法來修改文件。

use std::env;
extern crate dotenv;
use dotenv::dotenv;

use mongodb::{
bson::{extjson::de::Error, oid::ObjectId, doc}, //modify here
results::{ InsertOneResult},
Client, Collection,
};
use crate::models::user_model::User;

pub struct MongoRepo {
col: Collection<User>,
}

impl MongoRepo {
pub async fn init() -> Self {
//init code goes here
}

pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
//create_user code goes here
}

pub async fn get_user(&self, id: &String) -> Result<User, Error> {
let obj_id = ObjectId::parse_str(id).unwrap();
let filter = doc! {"_id": obj_id};
let user_detail = self
.col
.find_one(filter, None)
.await
.ok()
.expect("Error getting user's detail");
Ok(user_detail.unwrap())
}
}

上面的代碼片段執行以下操作:

其次,我們需要user_api.rs通過創建一個處理程序來進行修改,該處理程序使用 get_user中的方法來repository獲取用戶。

use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
use actix_web::{
post, get, //modify here
web::{Data, Json, Path}, //modify here
HttpResponse,
};

#[post("/user")]
pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
//create_user code goes here
}

#[get("/user/{id}")]
pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
let id = path.into_inner();
if id.is_empty() {
return HttpResponse::BadRequest().body("invalid ID");
}
let user_detail = db.get_user(&id).await;
match user_detail {
Ok(user) => HttpResponse::Ok().json(user),
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}

上面的代碼片段執行以下操作:

最后,我們需要修改應用程序入口點(main.rsget_user以通過導入處理程序并為其添加新服務來包含該處理程序。

mod api;
mod models;
mod repository;

//modify imports below
use actix_web::{web::Data, App, HttpServer};
use api::user_api::{create_user, get_user}; //import the handler here
use repository::mongodb_repo::MongoRepo;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db = MongoRepo::init().await;
let db_data = Data::new(db);
HttpServer::new(move || {
App::new()
.app_data(db_data.clone())
.service(create_user)
.service(get_user) //add this
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

編輯用戶端點
要編輯用戶,我們必須首先通過edit_user向實現塊mongodb_repo.rs添加方法來修改文件。

use std::env;
extern crate dotenv;
use dotenv::dotenv;

use mongodb::{
bson::{extjson::de::Error, oid::ObjectId, doc},
results::{ InsertOneResult, UpdateResult}, //modify here
Client, Collection,
};
use crate::models::user_model::User;

pub struct MongoRepo {
col: Collection<User>,
}

impl MongoRepo {
pub async fn init() -> Self {
//init code goes here
}

pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
//create_user code goes here
}

pub async fn get_user(&self, id: &String) -> Result<User, Error> {
//get_user code goes here
}

pub async fn update_user(&self, id: &String, new_user: User) -> Result<UpdateResult, Error> {
let obj_id = ObjectId::parse_str(id).unwrap();
let filter = doc! {"_id": obj_id};
let new_doc = doc! {
"$set":
{
"id": new_user.id,
"name": new_user.name,
"location": new_user.location,
"title": new_user.title
},
};
let updated_doc = self
.col
.update_one(filter, new_doc, None)
.await
.ok()
.expect("Error updating user");
Ok(updated_doc)
}
}

上面的代碼片段執行以下操作:

其次,我們需要user_api.rs通過創建一個處理程序來進行修改,該處理程序使用 中的update_user方法repository來更新用戶。

use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
use actix_web::{
post, get, put, //modify here
web::{Data, Json, Path},
HttpResponse,
};
use mongodb::bson::oid::ObjectId; //add this

#[post("/user")]
pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
//create_user code goes here
}

#[get("/user/{id}")]
pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
//get_user code goes here
}

#[put("/user/{id}")]
pub async fn update_user(
db: Data<MongoRepo>,
path: Path<String>,
new_user: Json<User>,
) -> HttpResponse {
let id = path.into_inner();
if id.is_empty() {
return HttpResponse::BadRequest().body("invalid ID");
};
let data = User {
id: Some(ObjectId::parse_str(&id).unwrap()),
name: new_user.name.to_owned(),
location: new_user.location.to_owned(),
title: new_user.title.to_owned(),
};
let update_result = db.update_user(&id, data).await;
match update_result {
Ok(update) => {
if update.matched_count == 1 {
let updated_user_info = db.get_user(&id).await;
return match updated_user_info {
Ok(user) => HttpResponse::Ok().json(user),
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
};
} else {
return HttpResponse::NotFound().body("No user found with specified ID");
}
}
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}

上面的代碼片段執行以下操作:

最后,我們需要修改應用程序入口點(main.rs)以通過導入處理程序并為其添加新服務來包含該處理程序。

 mod api;
mod models;
mod repository;

//modify imports below
use actix_web::{web::Data, App, HttpServer};
use api::user_api::{create_user, get_user, update_user}; //import the handler here
use repository::mongodb_repo::MongoRepo;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db = MongoRepo::init().await;
let db_data = Data::new(db);
HttpServer::new(move || {
App::new()
.app_data(db_data.clone())
.service(create_user)
.service(get_user)
.service(update_user) //add this
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

刪除用戶端點
要刪除用戶,我們必須首先通過delete_user向實現塊mongodb_repo.rs添加方法來修改文件。

use std::env;
extern crate dotenv;
use dotenv::dotenv;

use mongodb::{
bson::{extjson::de::Error, oid::ObjectId, doc},
results::{ InsertOneResult, UpdateResult, DeleteResult}, //modify here
Client, Collection,
};
use crate::models::user_model::User;

pub struct MongoRepo {
col: Collection<User>,
}

impl MongoRepo {
pub async fn init() -> Self {
//init code goes here
}

pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
//create_user code goes here
}

pub async fn get_user(&self, id: &String) -> Result<User, Error> {
//get_user code goes here
}

pub async fn update_user(&self, id: &String, new_user: User) -> Result<UpdateResult, Error> {
//update_user code goes here
}

pub async fn delete_user(&self, id: &String) -> Result<DeleteResult, Error> {
let obj_id = ObjectId::parse_str(id).unwrap();
let filter = doc! {"_id": obj_id};
let user_detail = self
.col
.delete_one(filter, None)
.await
.ok()
.expect("Error deleting user");
Ok(user_detail)
}
}

上面的代碼片段執行以下操作:

接下來,我們需要在user_api.rs中創建一個處理程序,這個處理程序會調用repository中的某個方法(我們假設這個方法命名為delete_user)來刪除指定的用戶。

use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
use actix_web::{
post, get, put, delete, //modify here
web::{Data, Json, Path},
HttpResponse,
};
use mongodb::bson::oid::ObjectId; //add this

#[post("/user")]
pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
//create_user code goes here
}

#[get("/user/{id}")]
pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
//get_user code goes here
}

#[put("/user/{id}")]
pub async fn update_user(
db: Data<MongoRepo>,
path: Path<String>,
new_user: Json<User>,
) -> HttpResponse {
//update_user code goes here
}

#[delete("/user/{id}")]
pub async fn delete_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
let id = path.into_inner();
if id.is_empty() {
return HttpResponse::BadRequest().body("invalid ID");
};
let result = db.delete_user(&id).await;
match result {
Ok(res) => {
if res.deleted_count == 1 {
return HttpResponse::Ok().json("User successfully deleted!");
} else {
return HttpResponse::NotFound().json("User with specified ID not found!");
}
}
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}

上面的代碼片段執行以下操作:

最后,我們需要修改應用程序入口點(main.rs)以通過導入處理程序并為其添加新服務來包含該處理程序。

mod api;
mod models;
mod repository;

//modify imports below
use actix_web::{web::Data, App, HttpServer};
use api::user_api::{create_user, get_user, update_user, delete_user}; //import the handler here
use repository::mongodb_repo::MongoRepo;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db = MongoRepo::init().await;
let db_data = Data::new(db);
HttpServer::new(move || {
App::new()
.app_data(db_data.clone())
.service(create_user)
.service(get_user)
.service(update_user)
.service(delete_user) //add this
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

獲取所有用戶端點
要獲取用戶列表,我們必須首先通過get_all_users向實現塊mongodb_repo.rs添加方法來修改文件。

 use std::env;
extern crate dotenv;
use dotenv::dotenv;

use mongodb::{
bson::{extjson::de::Error, oid::ObjectId, doc},
results::{ InsertOneResult, UpdateResult, DeleteResult},
Client, Collection,
};
use futures::stream::TryStreamExt; //add this
use crate::models::user_model::User;

pub struct MongoRepo {
col: Collection<User>,
}

impl MongoRepo {
pub async fn init() -> Self {
//init code goes here
}

pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
//create_user code goes here
}

pub async fn get_user(&self, id: &String) -> Result<User, Error> {
//get_user code goes here
}

pub async fn update_user(&self, id: &String, new_user: User) -> Result<UpdateResult, Error> {
//update_user code goes here
}

pub async fn delete_user(&self, id: &String) -> Result<DeleteResult, Error> {
//delete_user code goes here
}

pub async fn get_all_users(&self) -> Result<Vec<User>, Error> {
let mut cursors = self
.col
.find(None, None)
.await
.ok()
.expect("Error getting list of users");
let mut users: Vec<User> = Vec::new();
while let Some(user) = cursors
.try_next()
.await
.ok()
.expect("Error mapping through cursor")
{
users.push(user)
}
Ok(users)
}
}

上面的代碼片段添加了一個get_all_users方法,該方法接受 a self作為參數并返回用戶列表或錯誤。在方法內部,我們使用self引用結構MongoRepo體從集合中訪問find函數,無需任何過濾器,以便它可以匹配數據庫內的所有文檔,使用該try_next()方法循環遍歷用戶列表以最佳方式返回列表,并處理錯誤。

其次,我們需要在user_api.rs中創建一個新的處理程序,這個處理程序會利用repository中提供的get_all_users方法來獲取用戶列表。

use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
use actix_web::{
post, get, put, delete,
web::{Data, Json, Path},
HttpResponse,
};
use mongodb::bson::oid::ObjectId;

#[post("/user")]
pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
//create_user code goes here
}

#[get("/user/{id}")]
pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
//get_user code goes here
}

#[put("/user/{id}")]
pub async fn update_user(
db: Data<MongoRepo>,
path: Path<String>,
new_user: Json<User>,
) -> HttpResponse {
//update_user code goes here
}

#[delete("/user/{id}")]
pub async fn delete_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
//delet_user code goes here
}

#[get("/users")]
pub async fn get_all_users(db: Data<MongoRepo>) -> HttpResponse {
let users = db.get_all_users().await;
match users {
Ok(users) => HttpResponse::Ok().json(users),
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}

上面的代碼片段執行以下操作:

最終,我們需要更新應用程序的入口文件main.rs,通過導入新的處理程序,并將它們作為新的服務添加到服務集合中,以確保它們能夠被正確地路由和處理。

mod api;
mod models;
mod repository;

//modify imports below
use actix_web::{web::Data, App, HttpServer};
use api::user_api::{create_user, get_user, update_user, delete_user, get_all_users}; //import the handler here
use repository::mongodb_repo::MongoRepo;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db = MongoRepo::init().await;
let db_data = Data::new(db);
HttpServer::new(move || {
App::new()
.app_data(db_data.clone())
.service(create_user)
.service(get_user)
.service(update_user)
.service(delete_user)
.service(get_all_users)//add this
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

完成后,我們可以通過在終端中運行以下命令來測試我們的應用程序。

cargo run
創建用戶端點
獲取用戶端點
編輯用戶端點
刪除用戶端點
獲取用戶端點列表
包含用戶文檔的數據庫

結論

這篇文章討論了如何模塊化 Rust 應用程序、構建 REST API 以及使用 MongoDB 保存我們的數據。

原文鏈接:https://dev.to/hackmamba/build-a-rest-api-with-rust-and-mongodb-actix-web-version-ei1

上一篇:

如何通過API驅動的數字生態系統推動創新和效率

下一篇:

改變醫療保健:為醫療行業構建一個API市場
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費