从零到一:用 Rust 和 Actix-web 构建高性能 REST API

6 阅读1分钟

在当今的微服务架构和云原生时代,高性能、安全的 API 服务是每个后端开发者的必备技能。Rust 语言以其卓越的性能、内存安全和并发特性,正迅速成为构建关键基础设施的首选语言。本文将带你从零开始,使用 Rust 和 Actix-web 框架构建一个完整的 REST API 服务,涵盖从项目初始化到生产部署的全过程。

为什么选择 Rust 和 Actix-web?

Rust 的优势

  • 零成本抽象:高级特性不带来运行时开销
  • 内存安全:编译时保证内存安全,无数据竞争
  • 卓越性能:与 C/C++ 相当,远超其他高级语言
  • 强大的类型系统:减少运行时错误
  • 优秀的并发支持:无畏并发(Fearless Concurrency)

Actix-web 的特点

  • 高性能:基于 Actor 模型,支持异步/等待
  • 类型安全:充分利用 Rust 的类型系统
  • 丰富的生态:中间件、路由、WebSocket 等一应俱全
  • 生产就绪:被多家知名公司用于生产环境

项目实战:构建用户管理系统 API

1. 环境准备与项目初始化

首先确保安装了 Rust 工具链:

# 安装 Rust(如果尚未安装)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 创建新项目
cargo new user-management-api
cd user-management-api

# 添加依赖
cargo add actix-web
cargo add serde --features derive
cargo add serde_json
cargo add chrono --features serde
cargo add uuid --features "serde v4"
cargo add sqlx --features "runtime-tokio-rustls postgres"
cargo add dotenv
cargo add bcrypt
cargo add jsonwebtoken
cargo add validator --features derive

2. 项目结构设计

user-management-api/
├── src/
│   ├── main.rs          # 应用入口
│   ├── models/          # 数据模型
│   │   ├── mod.rs
│   │   ├── user.rs
│   │   └── response.rs
│   ├── handlers/        # 请求处理器
│   │   ├── mod.rs
│   │   └── user_handler.rs
│   ├── database/        # 数据库相关
│   │   ├── mod.rs
│   │   └── connection.rs
│   ├── middleware/      # 中间件
│   │   ├── mod.rs
│   │   └── auth.rs
│   ├── utils/           # 工具函数
│   │   ├── mod.rs
│   │   └── validation.rs
│   └── config/          # 配置管理
│       ├── mod.rs
│       └── settings.rs
├── migrations/          # 数据库迁移
│   └── 20240101000000_create_users.sql
├── .env.example        # 环境变量示例
├── .env                # 环境变量(本地开发)
├── Cargo.toml          # 依赖管理
└── Dockerfile          # 容器化配置

3. 核心代码实现

3.1 数据模型定义

// src/models/user.rs
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use uuid::Uuid;
use validator::Validate;

#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct User {
    pub id: Uuid,
    
    #[validate(email)]
    pub email: String,
    
    #[validate(length(min = 3, max = 50))]
    pub username: String,
    
    #[serde(skip_serializing)]
    pub password_hash: String,
    
    pub is_active: bool,
    pub is_admin: bool,
    
    #[serde(with = "chrono::serde::ts_seconds")]
    pub created_at: DateTime<Utc>,
    
    #[serde(with = "chrono::serde::ts_seconds")]
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Deserialize, Validate)]
pub struct CreateUserRequest {
    #[validate(email)]
    pub email: String,
    
    #[validate(length(min = 3, max = 50))]
    pub username: String,
    
    #[validate(length(min = 8))]
    pub password: String,
}

#[derive(Debug, Deserialize, Validate)]
pub struct UpdateUserRequest {
    #[validate(email)]
    pub email: Option<String>,
    
    #[validate(length(min = 3, max = 50))]
    pub username: Option<String>,
    
    #[validate(length(min = 8))]
    pub password: Option<String>,
}

#[derive(Debug, Deserialize, Validate)]
pub struct LoginRequest {
    #[validate(email)]
    pub email: String,
    
    #[validate(length(min = 8))]
    pub password: String,
}

3.2 数据库连接与迁移

// src/database/connection.rs
use sqlx::postgres::{PgPool, PgPoolOptions};
use std::time::Duration;
use crate::config::Settings;

pub type DbPool = PgPool;

pub async fn create_pool(settings: &Settings) -> Result<DbPool, sqlx::Error> {
    PgPoolOptions::new()
        .max_connections(settings.database.max_connections)
        .min_connections(settings.database.min_connections)
        .max_lifetime(Duration::from_secs(settings.database.max_lifetime))
        .connect_timeout(Duration::from_secs(settings.database.connect_timeout))
        .connect(&settings.database.url)
        .await
}

// migrations/20240101000000_create_users.sql
CREATE TABLE IF NOT EXISTS users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email VARCHAR(255) UNIQUE NOT NULL,
    username VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    is_active BOOLEAN DEFAULT true,
    is_admin BOOLEAN DEFAULT false,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);

3.3 用户处理器实现

// src/handlers/user_handler.rs
use actix_web::{web, HttpResponse, Responder};
use serde_json::json;
use crate::models::{User, CreateUserRequest, UpdateUserRequest};
use crate::database::DbPool;
use crate::utils::validation::validate_request;
use bcrypt::{hash, verify, DEFAULT_COST};
use uuid::Uuid;

pub struct UserHandler;

impl UserHandler {
    // 创建用户
    pub async fn create(
        pool: web::Data<DbPool>,
        user_data: web::Json<CreateUserRequest>,
    ) -> impl Responder {
        // 验证请求数据
        if let Err(errors) = validate_request(&user_data) {
            return HttpResponse::BadRequest().json(json!({
                "error": "Validation failed",
                "details": errors
            }));
        }
        
        // 检查邮箱是否已存在
        let existing_user = sqlx::query!("SELECT id FROM users WHERE email = $1", user_data.email)
            .fetch_optional(pool.get_ref())
            .await;
            
        if let Ok(Some(_)) = existing_user {
            return HttpResponse::Conflict().json(json!({
                "error": "Email already exists"
            }));
        }
        
        // 哈希密码
        let password_hash = match hash(&user_data.password, DEFAULT_COST) {
            Ok(hash) => hash,
            Err(_) => return HttpResponse::InternalServerError().finish(),
        };
        
        // 插入用户
        let user_id = Uuid::new_v4();
        match sqlx::query!(
            r#"
            INSERT INTO users (id, email, username, password_hash)
            VALUES ($1, $2, $3, $4)
            RETURNING id, email, username, is_active, is_admin, created_at, updated_at
            "#,
            user_id,
            user_data.email,
            user_data.username,
            password_hash
        )
        .fetch_one(pool.get_ref())
        .await
        {
            Ok(record) => {
                let user = User {
                    id: record.id,
                    email: record.email,
                    username: record.username,
                    password_hash: "".to_string(), // 不返回密码哈希
                    is_active: record.is_active,
                    is_admin: record.is_admin,
                    created_at: record.created_at,
                    updated_at: record.updated_at,
                };
                HttpResponse::Created().json(user)
            }
            Err(e) => {
                eprintln!("Database error: {}", e);
                HttpResponse::InternalServerError().finish()
            }
        }
    }
    
    // 获取用户列表(