当前位置: 首页 > news >正文

【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请求

删除数据
在这里插入图片描述

数据库查询,数据已被删除
在这里插入图片描述

http://www.lryc.cn/news/578326.html

相关文章:

  • Gige协议 Qt版使用文档仅供个人使用
  • Mac中如何Chrome禁用更新[update chflags macos]
  • RabbitMQ简单消息发送
  • Qt自定义外观详解
  • 大麦基于HarmonyOS星盾安全架构,打造全链路安全抢票方案
  • MySQL 中 InnoDB 存储引擎与 MyISAM 存储引擎的区别是什么?
  • PDFBox + Tess4J 从PDF中提取图片OCR识别文字
  • 发票PDF处理工具,智能识别合并一步到位
  • [特殊字符] 分享裂变新姿势:用 UniApp + Vue3 玩转小程序页面分享跳转!
  • .netcore+ef+redis+rabbitmq+dotcap先同步后异步再同步的方法,亲测有效
  • 植物small RNA靶基因预测软件,psRobot
  • 网络的相关概念
  • Java ArrayList顺序表 + 接口实现 + 底层
  • jQuery UI 安装使用教程
  • Electron 进程间通信(IPC)深度优化指南
  • mysql 双主集群故障修复
  • TensorFlow源码深度阅读指南
  • 清理 Docker 缓存占用
  • 第 1 课:Flask 简介与环境配置(Markdown 教案)
  • 深度解析基于贝叶斯的垃圾邮件分类
  • 如何让宿主机完全看不到Wi-Fi?虚拟机独立联网隐匿上网实战!
  • 基础算法合集-图论
  • 我认知的AI宇宙系列第三期
  • 贪心算法在C++中的应用与实践
  • 论文中用matplotlib画的图,如何保持大小一致。
  • 「Java案例」计算矩形面积
  • 嵌入式原理与应用篇---常见基础知识(10)
  • 湖北理元理律师事务所债务解法:从法律技术到生活重建
  • 大根堆加小根堆查找中位数o(N)时间复杂度
  • 【Springai】项目实战进度和规划