Actix-web是Rust生态中性能最强的Web框架之一,基于Actor模型构建。本文从零搭建一个REST API项目,覆盖路由、JSON处理、状态共享和中间件。
1. 项目搭建
cargo new actix-demo && cd actix-demo
编辑Cargo.toml:
[dependencies]
actix-web = "4"
actix-rt = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
2. 最简单的Hello World
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello, Actix-web!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(hello))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
HttpServer::new接收一个闭包,每个worker线程调用一次来创建App实例。默认worker数量等于CPU核心数。
3. 路由与路径参数
Actix-web支持多种路由方式:
use actix_web::{get, post, web};
// 方式1:属性宏
#[get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
let user_id = path.into_inner();
HttpResponse::Ok().body(format!("User ID: {}", user_id))
}
// 方式2:手动注册
async fn list_users() -> impl Responder {
HttpResponse::Ok().body("User list")
}
// 注册
App::new()
.service(get_user) // 属性宏方式
.route("/users", web::get().to(list_users)) // 手动方式
路径参数通过web::Path<T>提取。多个参数用元组:
#[get("/users/{user_id}/posts/{post_id}")]
async fn get_post(path: web::Path<(u32, u32)>) -> impl Responder {
let (user_id, post_id) = path.into_inner();
HttpResponse::Ok().body(format!("User {} Post {}", user_id, post_id))
}
4. JSON请求与响应
配合serde做序列化/反序列化:
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Serialize)]
struct UserResponse {
id: u64,
name: String,
email: String,
}
#[post("/users")]
async fn create_user(body: web::Json<CreateUser>) -> impl Responder {
let user = UserResponse {
id: 1,
name: body.name.clone(),
email: body.email.clone(),
};
HttpResponse::Created().json(user)
}
web::Json<T>自动解析请求体为T。返回时调用.json()自动序列化为JSON。Content-Type也自动设置。
5. 状态共享 —— web::Data
多个handler之间共享状态用web::Data包裹。Data内部用Arc实现线程安全:
use std::sync::Mutex;
struct AppState {
request_count: Mutex<u64>,
app_name: String,
}
#[get("/count")]
async fn count(data: web::Data<AppState>) -> impl Responder {
let mut counter = data.request_count.lock().unwrap();
*counter += 1;
HttpResponse::Ok().body(format!(
"{}: {} requests served",
data.app_name, counter
))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let state = web::Data::new(AppState {
request_count: Mutex::new(0),
app_name: "MyApp".to_string(),
});
HttpServer::new(move || {
App::new()
.app_data(state.clone())
.service(count)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
web::Data::new()在内部创建Arc,clone()只是增加引用计数。需要可变性的字段用Mutex或RwLock包裹。
6. 中间件
Actix-web的中间件通过wrap方法添加。内置的Logger中间件:
use actix_web::middleware::Logger;
use env_logger;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.route("/", web::get().to(hello))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
自定义中间件可以通过Transform trait实现,但更简单的方式是用wrap_fn:
use actix_web::dev::ServiceRequest;
App::new()
.wrap_fn(|req: ServiceRequest, srv| {
let start = std::time::Instant::now();
let method = req.method().clone();
let path = req.path().to_string();
let fut = srv.call(req);
async move {
let res = fut.await?;
let elapsed = start.elapsed();
println!("{} {} -> {} ({:?})", method, path, res.status(), elapsed);
Ok(res)
}
})
7. 错误处理
Actix-web提供ResponseError trait来统一错误处理:
use actix_web::error::ResponseError;
use std::fmt;
#[derive(Debug)]
enum ApiError {
NotFound(String),
Internal(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::Internal(msg) => write!(f, "Internal error: {}", msg),
}
}
}
impl ResponseError for ApiError {
fn error_response(&self) -> HttpResponse {
match self {
ApiError::NotFound(msg) => {
HttpResponse::NotFound().json(serde_json::json!({"error": msg}))
}
ApiError::Internal(msg) => {
HttpResponse::InternalServerError().json(serde_json::json!({"error": msg}))
}
}
}
}
handler返回Result<impl Responder, ApiError>即可自动转换错误。
小结
Actix-web的核心概念就这几个:App做路由组织,web::Path/Query/Json做参数提取,web::Data做状态共享,wrap做中间件。框架本身的抽象非常Rust风格——通过类型系统在编译期保证正确性。下一步可以接入数据库(diesel/sqlx)和认证中间件来构建完整的后端服务。