【Rust操作MySql】Actix Web 框架结合 MySQL 数据库进行交互
✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Rust开发,Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。所属的专栏:Rust语言通关之路
景天的主页:景天科技苑
文章目录
- Actix Web 框架操作MySql数据库
- 🛠️ 环境准备
- 添加依赖到 Cargo.toml
- 📁 项目结构(简单示例)
- 📦 初始化数据库连接(db.rs)
- 🔁 路由与处理逻辑(handlers.rs)
- 📄 主入口(main.rs)
- 完整的 Actix Web + MySQL + SQLx 实现 CRUD(增删改查)用户数据的示例项目
- ✅ 技术栈
- 🧱 数据库表结构
- 📁 项目结构建议
- 📦 依赖Cargo.toml
- 🔌 db.rs – 数据库连接池
- 📦 models.rs – 用户模型
- 🚀 handlers.rs – CRUD 实现
- 📡 routes.rs – 注册路由
- 🚀 main.rs
- 🔎 测试接口
- get请求
- post请求
- put请求
- delete请求
Actix Web 框架操作MySql数据库
在 Rust 中,使用 Actix Web 框架结合 MySQL 数据库进行交互,通常可以通过 sqlx、diesel、mysql_async 等库实现。
本文就使用 Actix Web + sqlx + MySQL 的最常见组合示例,包括设置、连接池、路由处理和查询执行等进行详细阐述。
🛠️ 环境准备
添加依赖到 Cargo.toml
[dependencies]
actix-web = "4.11.0"
dotenv = "0.15.0"
serde = "1.0.219"
serde_json = "1.0.140"
sqlx = { version = "0.8.6", features = ["mysql","tls-native-tls","runtime-tokio",
] }
📁 项目结构(简单示例)
src/
├── main.rs
├── db.rs # 数据库连接池模块
└── handlers.rs # 处理路由的逻辑
.env # 数据库环境变量
📄 .env 文件示例
DATABASE_URL=mysql://root:password@localhost:3306/my_database
📦 初始化数据库连接(db.rs)
use sqlx::mysql::MySqlPool; //数据库连接池
use std::env;// 初始化数据库连接池
pub async fn init_db_pool() -> MySqlPool {// 从环境变量中获取数据库连接字符串let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");// 创建连接池,传入连接字符串//注意异步,使用.awaitMySqlPool::connect(&database_url).await.expect("Failed to create pool")
}
表结构:
CREATE TABLE users (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100) NOT NULL,email VARCHAR(100) NOT NULL UNIQUE
);
表中数据
🔁 路由与处理逻辑(handlers.rs)
use actix_web::{ get, web, HttpResponse, Responder };
use sqlx::MySqlPool;
use serde::Serialize;#[derive(Serialize, sqlx::FromRow)]
struct User {id: i32,name: String,email: String,
}// 获取所有用户
#[get("/users")]
async fn get_users(pool: web::Data<MySqlPool>) -> impl Responder {// 从数据库中获取所有用户//query_as::<_, User> 会将查询结果映射到 User 结构体中//参数1:查询语句//参数2:查询结果映射到的结构体//fetch_all 会将查询结果转换为 Vec<User>let users = sqlx::query_as::<_, User>("SELECT id, name, email FROM users").fetch_all(pool.get_ref()).await;match users {Ok(users) => HttpResponse::Ok().json(users),Err(err) => {eprintln!("DB Error: {:?}", err);HttpResponse::InternalServerError().finish()}}
}// 初始化路由
pub fn init_routes(cfg: &mut web::ServiceConfig) {cfg.service(get_users);
}
📄 主入口(main.rs)
use actix_web::{ web, App, HttpServer };
use dotenv::dotenv;mod db;
mod handlers;#[actix_web::main]
async fn main() -> std::io::Result<()> {dotenv().ok(); // 加载 .env// 初始化数据库连接池let db_pool = db::init_db_pool().await;HttpServer::new(move || {// 将数据库连接池添加到应用上下文中App::new().app_data(web::Data::new(db_pool.clone())).configure(handlers::init_routes)}).bind(("127.0.0.1", 8080))?.run().await
}
运行服务,get请求访问,等拿到查询的数据json
✅ 小结
使用 sqlx 提供了异步、类型安全的数据库访问。
数据库连接池在 main.rs 中初始化,并通过 web::Data 注入路由中。
查询语句可用 query_as! 或 query_as::<_, T>() 实现结果自动映射到结构体。
完整的 Actix Web + MySQL + SQLx 实现 CRUD(增删改查)用户数据的示例项目
✅ 技术栈
Actix Web - Web 框架
SQLx - 异步数据库库,支持类型安全的查询
MySQL - 数据库
Serde - JSON 序列化/反序列化
dotenv - 加载环境变量
🧱 数据库表结构
CREATE TABLE users (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100) NOT NULL,email VARCHAR(100) NOT NULL UNIQUE
);
📁 项目结构建议
src/
├── main.rs
├── db.rs
├── handlers.rs
├── models.rs
└── routes.rs
.env
📦 依赖Cargo.toml
[dependencies]
actix-web = "4.11.0"
dotenv = "0.15.0"
serde = "1.0.219"
serde_json = "1.0.140"
sqlx = { version = "0.8.6", features = ["mysql","tls-native-tls","runtime-tokio",
] }
🔌 db.rs – 数据库连接池
use sqlx::mysql::MySqlPool; //数据库连接池
use std::env;// 初始化数据库连接池
pub async fn init_db_pool() -> MySqlPool {// 从环境变量中获取数据库连接字符串let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");// 创建连接池,传入连接字符串//注意异步,使用.awaitMySqlPool::connect(&database_url).await.expect("Failed to create pool")
}
📦 models.rs – 用户模型
use serde::{ Deserialize, Serialize };
use sqlx::FromRow; //这是sqlx的宏,用于将查询结果映射到结构体中//每个结构体都要用Serialize和FromRow两个宏修饰,分别用于序列化和映射
#[derive(Serialize, FromRow)]
pub struct User {pub id: i32,pub name: String,pub email: String,
}#[derive(Deserialize)]
pub struct CreateUser {pub name: String,pub email: String,
}#[derive(Deserialize)]
pub struct UpdateUser {pub name: Option<String>,pub email: Option<String>,
}
🚀 handlers.rs – CRUD 实现
use actix_web::{ web, HttpResponse, Responder };
use sqlx::MySqlPool;//注意,在非main.rs中,导入当前目录的文件模块,需要使用crate::模块名
use crate::models::{ User, CreateUser, UpdateUser };// 创建用户,增删改查函数都要有pool: web::Data<MySqlPool>参数,用于从应用上下文中获取数据库连接池
pub async fn create_user(pool: web::Data<MySqlPool>,data: web::Json<CreateUser>
) -> impl Responder {let result = sqlx::query("INSERT INTO users (name, email) VALUES (?, ?)").bind(data.name.clone()) //.bind(data.name.clone())绑定sql语句的第一个?占位符.bind(data.email.clone()).execute(pool.get_ref()).await; //.execute(pool.get_ref())执行sql语句,pool.get_ref()获取数据库连接池的引用//.await等待执行结果match result {Ok(_) => HttpResponse::Created().body("User created"),Err(e) => {eprintln!("Create error: {:?}", e);HttpResponse::InternalServerError().finish()}}
}// 获取所有用户
pub async fn get_users(pool: web::Data<MySqlPool>) -> impl Responder {let result = sqlx::query_as::<_, User>("SELECT id, name, email FROM users").fetch_all(pool.get_ref()).await;match result {Ok(users) => HttpResponse::Ok().json(users),Err(_) => HttpResponse::InternalServerError().finish(),}
}// 获取单个用户
pub async fn get_user(pool: web::Data<MySqlPool>, user_id: web::Path<i32>) -> impl Responder {let result = sqlx::query_as::<_, User>("SELECT id, name, email FROM users WHERE id = ?").bind(user_id.into_inner()).fetch_optional(pool.get_ref()).await;match result {Ok(Some(user)) => HttpResponse::Ok().json(user),Ok(None) => HttpResponse::NotFound().body("User not found"),Err(_) => HttpResponse::InternalServerError().finish(),}
}// 更新用户
pub async fn update_user(pool: web::Data<MySqlPool>,user_id: web::Path<i32>,data: web::Json<UpdateUser> //web::Json<UpdateUser> 会将请求体解析为 UpdateUser 结构体
) -> impl Responder {let user_id = user_id.into_inner();let existing = sqlx::query_as::<_, User>("SELECT id, name, email FROM users WHERE id = ?").bind(user_id).fetch_optional(pool.get_ref()).await; //.fetch_optional(pool.get_ref())获取单个结果,返回Option<User>if existing.is_err() || existing.as_ref().unwrap().is_none() {return HttpResponse::NotFound().body("User not found");}let user = existing.unwrap().unwrap(); //如果用户存在,unwrap()获取User,unwrap()获取Option<User>中的Userlet new_name = data.name.clone().unwrap_or(user.name.clone()); //unwrap_or()获取Option<String>中的String,如果Option<String>为None,则返回user.namelet new_email = data.email.clone().unwrap_or(user.email.clone());// 如果没有更改,返回 BadRequestif new_name == user.name && new_email == user.email {return HttpResponse::BadRequest().body("No changes");}// 更新用户let result = sqlx::query("UPDATE users SET name = ?, email = ? WHERE id = ?").bind(new_name).bind(new_email).bind(user_id).execute(pool.get_ref()).await;match result {Ok(_) => HttpResponse::Ok().body("User updated"),Err(_) => HttpResponse::InternalServerError().finish(),}
}// 删除用户
pub async fn delete_user(pool: web::Data<MySqlPool>, user_id: web::Path<i32>) -> impl Responder {let result = sqlx::query("DELETE FROM users WHERE id = ?").bind(user_id.into_inner()).execute(pool.get_ref()).await;match result {Ok(_) => HttpResponse::Ok().body("User deleted"),Err(_) => HttpResponse::InternalServerError().finish(),}
}
🔍 说明:
query(…):只传入 SQL 字符串
.bind(&data.name):绑定第一个占位符 ?
.bind(&data.email):绑定第二个占位符
.execute(…):传入连接池执行
🔍 query vs query_as
✅ 总结一句话:
用 query() 👉 当你不需要映射结构体时(如插入、删除、更新)
用 query_as::<_, YourStruct>() 👉 当你需要将查询结果映射成结构体时(如 select 查询)
📡 routes.rs – 注册路由
use actix_web::web;// 导入处理函数
use crate::handlers::*;pub fn config(cfg: &mut web::ServiceConfig) {cfg.service(web::scope("/users") //scope用来定义路由前缀.route("", web::post().to(create_user)).route("", web::get().to(get_users)).route("/{id}", web::get().to(get_user)).route("/{id}", web::put().to(update_user)).route("/{id}", web::delete().to(delete_user)));
}
🚀 main.rs
use actix_web::{ App, HttpServer, web };
use dotenv::dotenv;mod db;
mod handlers;
mod models;
mod routes;#[actix_web::main]
async fn main() -> std::io::Result<()> {dotenv().ok();// 初始化数据库连接池let pool = db::init_db_pool().await;HttpServer::new(move || {// 将数据库连接池添加到应用上下文中App::new().app_data(web::Data::new(pool.clone())).configure(routes::config)}).bind(("127.0.0.1", 8080))?.run().await
}
🔎 测试接口
get请求
查询所有用户
查询指定用户:
post请求
创建用户
数据库中查看,创建成功
put请求
更新用户
查询邮箱已更新
如果数据与原数据相同,则返回400错误码
delete请求
删除数据
数据库查询,数据已被删除