Actix-Web 框架实战:构建高性能 RESTful API 服务
目 录
- 📝 摘要
- 一、Actix-Web 简介
- 1.1 为什么选择 Actix-Web?
- 1.2 快速开始
- 二、路由系统详解
- 2.1 基础路由
- 2.2 查询参数与表单处理
- 2.3 路由组织(Scope)
- 三、应用状态管理
- 3.1 共享状态(App Data)
- 3.2 数据库连接池
- 四、中间件开发
- 4.1 日志中间件
- 4.2 认证中间件
- 4.3 CORS 配置
- 五、完整项目实战:博客 API
- 5.1 项目结构
- 5.2 数据模型
- 5.3 数据库操作
- 5.4 请求处理器
- 5.5 主程序
- 六、错误处理
- 6.1 自定义错误类型
- 七、性能优化
- 7.1 连接池配置
- 7.2 响应压缩
- 八、总结与讨论
- 参考链接
📝 摘要
Actix-Web 是 Rust 生态中性能最强大的 Web 框架之一,基于 Actor 模型和 Tokio 异步运行时。本文将深入讲解 Actix-Web 的核心架构、路由系统、中间件机制以及数据库集成,通过完整的 RESTful API 项目实战,帮助读者掌握使用 Rust 构建生产级 Web 服务的全流程。
一、Actix-Web 简介
1.1 为什么选择 Actix-Web?
性能对比(TechEmpower Benchmark):
| 框架 | 语言 | 吞吐量(req/sec) | 延迟(ms) |
|---|---|---|---|
| Actix-Web | Rust | 700万+ | <1 |
| Axum | Rust | 650万+ | <1 |
| FastAPI | Python | 10万+ | 5-10 |
| Express.js | Node.js | 15万+ | 3-8 |
| Spring Boot | Java | 20万+ | 5-15 |
核心优势:

1.2 快速开始
# Cargo.toml
[package]
name = "actix-web-demo"
version = "0.1.0"
edition = "2021"[dependencies]
actix-web = "4.4"
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Hello World 示例:
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};#[get("/")]
async fn hello() -> impl Responder {HttpResponse::Ok().body("Hello, Actix-Web! 🦀")
}#[actix_web::main]
async fn main() -> std::io::Result<()> {println!("🚀 服务器启动在 http://127.0.0.1:8080");HttpServer::new(|| {App::new().service(hello)}).bind(("127.0.0.1", 8080))?.run().await
}
测试运行:
cargo run# 访问 http://127.0.0.1:8080
# 输出:Hello, Actix-Web! 🦀
二、路由系统详解
2.1 基础路由
use actix_web::{get, post, put, delete, web, HttpResponse};
use serde::{Deserialize, Serialize};#[derive(Serialize, Deserialize)]
struct User {id: u32,name: String,email: String,
}// GET 请求
#[get("/users")]
async fn get_users() -> HttpResponse {let users = vec![User {id: 1,name: "Alice".to_string(),email: "alice@example.com".to_string(),},User {id: 2,name: "Bob".to_string(),email: "bob@example.com".to_string(),},];HttpResponse::Ok().json(users)
}// GET 请求(带路径参数)
#[get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> HttpResponse {let user_id = path.into_inner();let user = User {id: user_id,name: "Alice".to_string(),email: "alice@example.com".to_string(),};HttpResponse::Ok().json(user)
}// POST 请求(创建资源)
#[post("/users")]
async fn create_user(user: web::Json<User>) -> HttpResponse {println!("创建用户: {:?}", user.name);HttpResponse::Created().json(user.into_inner())
}// PUT 请求(更新资源)
#[put("/users/{id}")]
async fn update_user(path: web::Path<u32>,user: web::Json<User>,
) -> HttpResponse {let user_id = path.into_inner();println!("更新用户 {}: {:?}", user_id, user.name);HttpResponse::Ok().json(user.into_inner())
}// DELETE 请求
#[delete("/users/{id}")]
async fn delete_user(path: web::Path<u32>) -> HttpResponse {let user_id = path.into_inner();println!("删除用户: {}", user_id);HttpResponse::NoContent().finish()
}
2.2 查询参数与表单处理
use actix_web::{web, HttpResponse};
use serde::Deserialize;#[derive(Deserialize)]
struct QueryParams {page: Option<u32>,limit: Option<u32>,search: Option<String>,
}// 查询参数
#[get("/search")]
async fn search(query: web::Query<QueryParams>) -> HttpResponse {let page = query.page.unwrap_or(1);let limit = query.limit.unwrap_or(10);let search_term = query.search.as_deref().unwrap_or("");HttpResponse::Ok().json(serde_json::json!({"page": page,"limit": limit,"search": search_term,"results": []}))
}// 表单数据
#[derive(Deserialize)]
struct LoginForm {username: String,password: String,
}#[post("/login")]
async fn login(form: web::Form<LoginForm>) -> HttpResponse {println!("登录用户: {}", form.username);// 模拟验证if form.username == "admin" && form.password == "password" {HttpResponse::Ok().json(serde_json::json!({"status": "success","token": "mock-jwt-token"}))} else {HttpResponse::Unauthorized().json(serde_json::json!({"status": "error","message": "Invalid credentials"}))}
}
2.3 路由组织(Scope)
use actix_web::{web, App, HttpServer};#[actix_web::main]
async fn main() -> std::io::Result<()> {HttpServer::new(|| {App::new()// API v1 路由组.service(web::scope("/api/v1").service(web::scope("/users").service(get_users).service(get_user).service(create_user).service(update_user).service(delete_user)).service(web::scope("/posts").route("", web::get().to(get_posts)).route("/{id}", web::get().to(get_post))))// API v2 路由组.service(web::scope("/api/v2").service(get_users))}).bind(("127.0.0.1", 8080))?.run().await
}async fn get_posts() -> HttpResponse {HttpResponse::Ok().json(Vec::<String>::new())
}async fn get_post() -> HttpResponse {HttpResponse::Ok().json(serde_json::json!({}))
}
路由结构可视化:
/api/v1├── /users│ ├── GET / (获取用户列表)│ ├── GET /{id} (获取单个用户)│ ├── POST / (创建用户)│ ├── PUT /{id} (更新用户)│ └── DELETE /{id} (删除用户)└── /posts├── GET / (获取文章列表)└── GET /{id} (获取单篇文章)
三、应用状态管理
3.1 共享状态(App Data)
use actix_web::{web, App, HttpResponse, HttpServer};
use std::sync::Mutex;struct AppState {counter: Mutex<i32>,app_name: String,
}#[get("/count")]
async fn get_count(data: web::Data<AppState>) -> HttpResponse {let count = data.counter.lock().unwrap();HttpResponse::Ok().json(serde_json::json!({"app": data.app_name,"count": *count}))
}#[post("/increment")]
async fn increment(data: web::Data<AppState>) -> HttpResponse {let mut count = data.counter.lock().unwrap();*count += 1;HttpResponse::Ok().json(serde_json::json!({"new_count": *count}))
}#[actix_web::main]
async fn main() -> std::io::Result<()> {let app_state = web::Data::new(AppState {counter: Mutex::new(0),app_name: String::from("Actix-Web Demo"),});HttpServer::new(move || {App::new().app_data(app_state.clone()).service(get_count).service(increment)}).bind(("127.0.0.1", 8080))?.run().await
}
3.2 数据库连接池
# Cargo.toml
[dependencies]
sqlx = { version = "0.7", features = ["runtime-tokio-native-tls", "sqlite"] }
use actix_web::{web, App, HttpResponse, HttpServer};
use sqlx::{SqlitePool, FromRow};#[derive(FromRow, serde::Serialize)]
struct User {id: i64,name: String,email: String,
}#[get("/users")]
async fn get_users_from_db(pool: web::Data<SqlitePool>) -> HttpResponse {let users = sqlx::query_as::<_, User>("SELECT * FROM users").fetch_all(pool.get_ref()).await;match users {Ok(users) => HttpResponse::Ok().json(users),Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()}))}
}#[actix_web::main]
async fn main() -> std::io::Result<()> {// 创建数据库连接池let pool = SqlitePool::connect("sqlite::memory:").await.expect("Failed to create pool");// 创建表sqlx::query("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY,name TEXT NOT NULL,email TEXT NOT NULL)").execute(&pool).await.expect("Failed to create table");HttpServer::new(move || {App::new().app_data(web::Data::new(pool.clone())).service(get_users_from_db)}).bind(("127.0.0.1", 8080))?.run().await
}
四、中间件开发
4.1 日志中间件
use actix_web::{dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},Error, HttpMessage,
};
use futures_util::future::LocalBoxFuture;
use std::future::{ready, Ready};
use std::time::Instant;pub struct Logger;impl<S, B> Transform<S, ServiceRequest> for Logger
whereS: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,S::Future: 'static,B: 'static,
{type Response = ServiceResponse<B>;type Error = Error;type InitError = ();type Transform = LoggerMiddleware<S>;type Future = Ready<Result<Self::Transform, Self::InitError>>;fn new_transform(&self, service: S) -> Self::Future {ready(Ok(LoggerMiddleware { service }))}
}pub struct LoggerMiddleware<S> {service: S,
}impl<S, B> Service<ServiceRequest> for LoggerMiddleware<S>
whereS: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,S::Future: 'static,B: 'static,
{type Response = ServiceResponse<B>;type Error = Error;type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;forward_ready!(service);fn call(&self, req: ServiceRequest) -> Self::Future {let start = Instant::now();let method = req.method().to_string();let path = req.path().to_string();let fut = self.service.call(req);Box::pin(async move {let res = fut.await?;let elapsed = start.elapsed();println!("📊 {} {} - {} - {:?}",method,path,res.status(),elapsed);Ok(res)})}
}// 使用中间件
#[actix_web::main]
async fn main() -> std::io::Result<()> {HttpServer::new(|| {App::new().wrap(Logger) // 应用日志中间件.service(hello)}).bind(("127.0.0.1", 8080))?.run().await
}
4.2 认证中间件
use actix_web::{dev::{ServiceRequest, ServiceResponse},error::ErrorUnauthorized,HttpMessage,
};pub struct AuthMiddleware;impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
whereS: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,S::Future: 'static,B: 'static,
{// ... (类似Logger的实现)fn call(&self, req: ServiceRequest) -> Self::Future {// 检查 Authorization headerlet auth_header = req.headers().get("Authorization");if let Some(auth) = auth_header {if let Ok(auth_str) = auth.to_str() {if auth_str.starts_with("Bearer ") {let token = &auth_str[7..];// 验证 token(示例)if token == "valid-token" {req.extensions_mut().insert("user_id".to_string());return Box::pin(self.service.call(req));}}}}Box::pin(async move {Err(ErrorUnauthorized("Invalid or missing token"))})}
}
4.3 CORS 配置
[dependencies]
actix-cors = "0.7"
use actix_cors::Cors;
use actix_web::{http, App, HttpServer};#[actix_web::main]
async fn main() -> std::io::Result<()> {HttpServer::new(|| {let cors = Cors::default().allowed_origin("http://localhost:3000").allowed_methods(vec!["GET", "POST", "PUT", "DELETE"]).allowed_headers(vec![http::header::AUTHORIZATION, http::header::CONTENT_TYPE]).max_age(3600);App::new().wrap(cors).service(hello)}).bind(("127.0.0.1", 8080))?.run().await
}
五、完整项目实战:博客 API
5.1 项目结构
blog-api/
├── Cargo.toml
├── src/
│ ├── main.rs
│ ├── models/
│ │ ├── mod.rs
│ │ ├── user.rs
│ │ └── post.rs
│ ├── handlers/
│ │ ├── mod.rs
│ │ ├── user_handler.rs
│ │ └── post_handler.rs
│ ├── db.rs
│ └── middleware/
│ ├── mod.rs
│ └── auth.rs
5.2 数据模型
// src/models/user.rs
use serde::{Deserialize, Serialize};
use sqlx::FromRow;#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct User {pub id: i64,pub username: String,pub email: String,pub created_at: String,
}#[derive(Debug, Deserialize)]
pub struct CreateUser {pub username: String,pub email: String,pub password: String,
}// src/models/post.rs
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct Post {pub id: i64,pub title: String,pub content: String,pub author_id: i64,pub created_at: String,
}#[derive(Debug, Deserialize)]
pub struct CreatePost {pub title: String,pub content: String,
}
5.3 数据库操作
// src/db.rs
use sqlx::{SqlitePool, Result};
use crate::models::{User, CreateUser, Post, CreatePost};pub async fn create_user(pool: &SqlitePool, user: CreateUser) -> Result<User> {let result = sqlx::query_as::<_, User>("INSERT INTO users (username, email, password_hash, created_at) VALUES (?, ?, ?, datetime('now')) RETURNING id, username, email, created_at").bind(&user.username).bind(&user.email).bind(&user.password) // 实际项目中应该使用哈希.fetch_one(pool).await?;Ok(result)
}pub async fn get_user_by_id(pool: &SqlitePool, id: i64) -> Result<Option<User>> {let user = sqlx::query_as::<_, User>("SELECT id, username, email, created_at FROM users WHERE id = ?").bind(id).fetch_optional(pool).await?;Ok(user)
}pub async fn create_post(pool: &SqlitePool,post: CreatePost,author_id: i64,
) -> Result<Post> {let result = sqlx::query_as::<_, Post>("INSERT INTO posts (title, content, author_id, created_at) VALUES (?, ?, ?, datetime('now')) RETURNING id, title, content, author_id, created_at").bind(&post.title).bind(&post.content).bind(author_id).fetch_one(pool).await?;Ok(result)
}pub async fn get_posts(pool: &SqlitePool, limit: i64) -> Result<Vec<Post>> {let posts = sqlx::query_as::<_, Post>("SELECT id, title, content, author_id, created_at FROM posts ORDER BY created_at DESC LIMIT ?").bind(limit).fetch_all(pool).await?;Ok(posts)
}
5.4 请求处理器
// src/handlers/user_handler.rs
use actix_web::{web, HttpResponse, Result};
use sqlx::SqlitePool;
use crate::{db, models::CreateUser};pub async fn create_user_handler(pool: web::Data<SqlitePool>,user: web::Json<CreateUser>,
) -> Result<HttpResponse> {match db::create_user(pool.get_ref(), user.into_inner()).await {Ok(user) => Ok(HttpResponse::Created().json(user)),Err(e) => Ok(HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})))}
}pub async fn get_user_handler(pool: web::Data<SqlitePool>,user_id: web::Path<i64>,
) -> Result<HttpResponse> {match db::get_user_by_id(pool.get_ref(), user_id.into_inner()).await {Ok(Some(user)) => Ok(HttpResponse::Ok().json(user)),Ok(None) => Ok(HttpResponse::NotFound().json(serde_json::json!({"error": "User not found"}))),Err(e) => Ok(HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})))}
}// src/handlers/post_handler.rs
pub async fn create_post_handler(pool: web::Data<SqlitePool>,post: web::Json<CreatePost>,req: HttpRequest,
) -> Result<HttpResponse> {// 从请求扩展中获取认证的用户IDlet author_id = req.extensions().get::<i64>().copied().unwrap_or(1);match db::create_post(pool.get_ref(), post.into_inner(), author_id).await {Ok(post) => Ok(HttpResponse::Created().json(post)),Err(e) => Ok(HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})))}
}pub async fn get_posts_handler(pool: web::Data<SqlitePool>,query: web::Query<HashMap<String, String>>,
) -> Result<HttpResponse> {let limit = query.get("limit").and_then(|s| s.parse().ok()).unwrap_or(10);match db::get_posts(pool.get_ref(), limit).await {Ok(posts) => Ok(HttpResponse::Ok().json(posts)),Err(e) => Ok(HttpResponse::InternalServerError().json(serde_json::json!({"error": e.to_string()})))}
}
5.5 主程序
// src/main.rs
mod models;
mod handlers;
mod db;
mod middleware;use actix_web::{web, App, HttpServer, middleware::Logger};
use sqlx::SqlitePool;
use env_logger::Env;#[actix_web::main]
async fn main() -> std::io::Result<()> {// 初始化日志env_logger::init_from_env(Env::default().default_filter_or("info"));// 创建数据库连接池let database_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite:blog.db".to_string());let pool = SqlitePool::connect(&database_url).await.expect("Failed to create pool");// 运行数据库迁移sqlx::migrate!("./migrations").run(&pool).await.expect("Failed to run migrations");println!("🚀 服务器启动在 http://127.0.0.1:8080");HttpServer::new(move || {App::new().wrap(Logger::default()).app_data(web::Data::new(pool.clone())).service(web::scope("/api").service(web::scope("/users").route("", web::post().to(handlers::create_user_handler)).route("/{id}", web::get().to(handlers::get_user_handler))).service(web::scope("/posts").route("", web::get().to(handlers::get_posts_handler)).route("", web::post().to(handlers::create_post_handler))))}).bind(("127.0.0.1", 8080))?.workers(4) // 工作线程数.run().await
}
六、错误处理
6.1 自定义错误类型
use actix_web::{error::ResponseError, http::StatusCode, HttpResponse};
use std::fmt;#[derive(Debug)]
pub enum ApiError {NotFound(String),BadRequest(String),InternalError(String),
}impl fmt::Display for ApiError {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {ApiError::NotFound(msg) => write!(f, "Not Found: {}", msg),ApiError::BadRequest(msg) => write!(f, "Bad Request: {}", msg),ApiError::InternalError(msg) => write!(f, "Internal Error: {}", msg),}}
}impl ResponseError for ApiError {fn error_response(&self) -> HttpResponse {let (status, message) = match self {ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),ApiError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),ApiError::InternalError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),};HttpResponse::build(status).json(serde_json::json!({"error": message}))}
}// 使用示例
async fn handler() -> Result<HttpResponse, ApiError> {Err(ApiError::NotFound("Resource not found".to_string()))
}
七、性能优化
7.1 连接池配置
let pool = SqlitePool::connect_with(SqliteConnectOptions::from_str(&database_url)?.max_connections(5).min_connections(1).connect_timeout(Duration::from_secs(8))
)
.await?;
7.2 响应压缩
[dependencies]
actix-web = { version = "4.4", features = ["compress-gzip"] }
use actix_web::middleware::Compress;App::new().wrap(Compress::default()).service(hello)
八、总结与讨论
Actix-Web 是构建高性能 Web 服务的利器,关键要点包括:
✅ 卓越性能:接近硬件极限的吞吐量
✅ 类型安全:编译期捕获大部分错误
✅ 灵活路由:强大的路由和中间件系统
✅ 异步支持:基于 Tokio 的高并发能力
讨论问题:
- Actix-Web vs Axum:如何选择?
- 生产环境中如何监控 Rust Web 服务?
- 如何设计 RESTful API 的版本管理?
欢迎分享经验!🚀
参考链接
- Actix-Web 官方文档:https://actix.rs
- SQLx 文档:https://docs.rs/sqlx
- TechEmpower Benchmark:https://www.techempower.com/benchmarks/
