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

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-WebRust700万+<1
AxumRust650万+<1
FastAPIPython10万+5-10
Express.jsNode.js15万+3-8
Spring BootJava20万+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 的高并发能力

讨论问题

  1. Actix-Web vs Axum:如何选择?
  2. 生产环境中如何监控 Rust Web 服务?
  3. 如何设计 RESTful API 的版本管理?

欢迎分享经验!🚀


参考链接

  1. Actix-Web 官方文档:https://actix.rs
  2. SQLx 文档:https://docs.rs/sqlx
  3. TechEmpower Benchmark:https://www.techempower.com/benchmarks/

http://www.dtcms.com/a/544541.html

相关文章:

  • 边缘计算中针对不同类型的任务(如分类、回归)评估指标
  • 【16】C语言-编辑`task.json`文件以改变警告等级
  • Rust高性能优化与零拷贝技术深度实践
  • Linux-Redhat系统启动读取文件流程
  • React 10
  • 京东方 EV101WXM-N10 工业液晶模组技术摘要
  • Deep End-to-End Alignment and Refinement for Time-of-Flight RGB-D modules复现
  • Java-163 MongoDB 生产安全加固实战:10 分钟完成认证、最小权限、角色详解
  • MinIO 与云原生_现代化对象存储解决方案
  • 【C语言实战(63)】从0到1:51单片机GPIO控制实战秘籍
  • 金仓替代MongoDB:互联网医院聊天脱敏实战
  • 使用 ESLint + Prettier + Husky
  • mongodb备份脚本(单机+副本集)
  • 金仓数据库平替MongoDB全栈安全实战:从文档存储到多模一体化的演进之路
  • 基于k8s环境下mongodb备份恢复实战
  • 申威ky10架构安装MongoDB 4.0.1(rpm包:mongodb-4.0.1-8.ky10.sw_64.rpm)详细步骤
  • 网站建设开发语言和使用工具it培训套路
  • Diffusion Model与视频超分(2):解读字节开源视频增强模型SeedVR2
  • Linux小课堂: 系统硬件资源管理与设备操作指南
  • ROS2核心概念之代码示例一
  • 工厂考勤系统选型参考:如何选出最合适的方案
  • 【前端小站】CSS 样式美学:从基础语法到界面精筑的实战宝典
  • Linux错误(7)接口处于Down状态不通告IPv6地址变更事件
  • 开发避坑指南(67):Maven引入iText7-core依赖失败解决方案
  • 北京南站地图建设公司简介怎么写
  • 镇江网站建设平台江苏企业展厅设计公司
  • 用JetBrains Rider开发C#应用程序指南
  • 快速创建Word箱单(2/2)
  • LangChain4j学习11:模型上下文协议 (MCP)
  • mysql线上主从集群设置