Rust Web开发:Actix-web入门

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()在内部创建Arcclone()只是增加引用计数。需要可变性的字段用MutexRwLock包裹。

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)和认证中间件来构建完整的后端服务。