Files
rust-user-api/implementation_details.md
enoch 28afc7532f feat: [阶段1] 项目初始化和基础设置
- 创建 Cargo.toml 配置文件,包含所有必要依赖
- 建立完整的项目模块结构(config, models, handlers, routes, services, storage, middleware, utils)
- 实现用户数据模型和内存存储
- 创建基础的 HTTP 处理器和路由配置
- 添加错误处理和 JWT 认证中间件
- 配置环境变量和日志系统
- 创建项目文档和学习指南
- 服务器可以成功编译和启动
2025-08-04 16:49:50 +08:00

16 KiB
Raw Blame History

Rust REST API Server - 详细实现指南

阶段 1: 项目初始化和基础设置

1.1 创建项目和基础配置

目标: 建立 Rust 项目基础结构和依赖管理

具体步骤:

  1. 使用 cargo new rust-user-api --bin 创建新项目
  2. 配置 Cargo.toml 文件,添加必要依赖
  3. 创建基础目录结构
  4. 设置开发环境配置

Cargo.toml 配置:

[package]
name = "rust-user-api"
version = "0.1.0"
edition = "2021"

[dependencies]
# Web 框架
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }

# 序列化
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

# 时间处理
chrono = { version = "0.4", features = ["serde"] }

# UUID 生成
uuid = { version = "1.0", features = ["v4", "serde"] }

# 环境变量
dotenv = "0.15"

# 日志
tracing = "0.1"
tracing-subscriber = "0.3"

# HTTP 客户端(用于测试)
[dev-dependencies]
reqwest = { version = "0.11", features = ["json"] }

学习重点:

  • Cargo 包管理器的使用
  • Rust 项目结构约定
  • 依赖版本管理和特性选择

阶段 2: 创建基本的 HTTP Server 和路由结构

2.1 实现基础 HTTP 服务器

目标: 创建一个能够响应 HTTP 请求的基础服务器

核心文件:

  • src/main.rs - 应用入口点
  • src/lib.rs - 库入口
  • src/routes/mod.rs - 路由配置

main.rs 实现:

use axum::{
    routing::get,
    Router,
    response::Json,
};
use serde_json::{json, Value};
use std::net::SocketAddr;
use tracing_subscriber;

#[tokio::main]
async fn main() {
    // 初始化日志
    tracing_subscriber::init();

    // 创建路由
    let app = Router::new()
        .route("/", get(root))
        .route("/health", get(health_check));

    // 启动服务器
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("Server running on http://{}", addr);
    
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn root() -> Json<Value> {
    Json(json!({
        "message": "Welcome to Rust User API",
        "version": "0.1.0"
    }))
}

async fn health_check() -> Json<Value> {
    Json(json!({
        "status": "healthy",
        "timestamp": chrono::Utc::now()
    }))
}

学习重点:

  • Axum 框架基础概念
  • 异步编程 (async/await)
  • 路由定义和处理器函数
  • JSON 响应处理

2.2 模块化路由结构

routes/mod.rs 实现:

use axum::{Router, routing::get};
use crate::handlers;

pub fn create_routes() -> Router {
    Router::new()
        .route("/", get(handlers::root))
        .route("/health", get(handlers::health_check))
        .nest("/api", api_routes())
}

fn api_routes() -> Router {
    Router::new()
        .route("/users", get(handlers::user::list_users))
        // 后续添加更多路由
}

学习重点:

  • 模块系统和代码组织
  • 路由嵌套和分组
  • 处理器函数的分离

阶段 3: 实现用户数据模型和内存存储

3.1 定义数据模型

目标: 创建类型安全的数据模型和序列化支持

models/user.rs 实现:

use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    pub id: Uuid,
    pub username: String,
    pub email: String,
    pub password_hash: String,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Serialize)]
pub struct UserResponse {
    pub id: Uuid,
    pub username: String,
    pub email: String,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
    pub username: String,
    pub email: String,
    pub password: String,
}

#[derive(Debug, Deserialize)]
pub struct UpdateUserRequest {
    pub username: Option<String>,
    pub email: Option<String>,
}

impl From<User> for UserResponse {
    fn from(user: User) -> Self {
        UserResponse {
            id: user.id,
            username: user.username,
            email: user.email,
            created_at: user.created_at,
        }
    }
}

学习重点:

  • Rust 结构体定义
  • Serde 序列化和反序列化
  • 类型转换和 From trait
  • 可选字段处理

3.2 实现内存存储

storage/memory.rs 实现:

use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use uuid::Uuid;
use crate::models::user::User;

pub type UserStorage = Arc<RwLock<HashMap<Uuid, User>>>;

#[derive(Clone)]
pub struct MemoryUserStore {
    users: UserStorage,
}

impl MemoryUserStore {
    pub fn new() -> Self {
        Self {
            users: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub async fn create_user(&self, user: User) -> Result<User, String> {
        let mut users = self.users.write().unwrap();
        users.insert(user.id, user.clone());
        Ok(user)
    }

    pub async fn get_user(&self, id: &Uuid) -> Option<User> {
        let users = self.users.read().unwrap();
        users.get(id).cloned()
    }

    pub async fn list_users(&self) -> Vec<User> {
        let users = self.users.read().unwrap();
        users.values().cloned().collect()
    }

    pub async fn update_user(&self, id: &Uuid, updated_user: User) -> Option<User> {
        let mut users = self.users.write().unwrap();
        users.insert(*id, updated_user.clone());
        Some(updated_user)
    }

    pub async fn delete_user(&self, id: &Uuid) -> bool {
        let mut users = self.users.write().unwrap();
        users.remove(id).is_some()
    }
}

学习重点:

  • HashMap 数据结构
  • 线程安全 (Arc<RwLock<T>>)
  • 异步方法定义
  • 错误处理基础

阶段 4: 实现用户 CRUD API 端点

4.1 创建处理器函数

handlers/user.rs 实现:

use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::Json,
    Json as RequestJson,
};
use uuid::Uuid;
use chrono::Utc;

use crate::models::user::{User, UserResponse, CreateUserRequest, UpdateUserRequest};
use crate::storage::memory::MemoryUserStore;

pub async fn create_user(
    State(store): State<MemoryUserStore>,
    RequestJson(payload): RequestJson<CreateUserRequest>,
) -> Result<(StatusCode, Json<UserResponse>), StatusCode> {
    let user = User {
        id: Uuid::new_v4(),
        username: payload.username,
        email: payload.email,
        password_hash: hash_password(&payload.password),
        created_at: Utc::now(),
        updated_at: Utc::now(),
    };

    match store.create_user(user).await {
        Ok(user) => Ok((StatusCode::CREATED, Json(user.into()))),
        Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
    }
}

pub async fn get_user(
    State(store): State<MemoryUserStore>,
    Path(id): Path<Uuid>,
) -> Result<Json<UserResponse>, StatusCode> {
    match store.get_user(&id).await {
        Some(user) => Ok(Json(user.into())),
        None => Err(StatusCode::NOT_FOUND),
    }
}

pub async fn list_users(
    State(store): State<MemoryUserStore>,
) -> Json<Vec<UserResponse>> {
    let users = store.list_users().await;
    let responses: Vec<UserResponse> = users.into_iter().map(|u| u.into()).collect();
    Json(responses)
}

pub async fn update_user(
    State(store): State<MemoryUserStore>,
    Path(id): Path<Uuid>,
    RequestJson(payload): RequestJson<UpdateUserRequest>,
) -> Result<Json<UserResponse>, StatusCode> {
    match store.get_user(&id).await {
        Some(mut user) => {
            if let Some(username) = payload.username {
                user.username = username;
            }
            if let Some(email) = payload.email {
                user.email = email;
            }
            user.updated_at = Utc::now();

            match store.update_user(&id, user).await {
                Some(updated_user) => Ok(Json(updated_user.into())),
                None => Err(StatusCode::INTERNAL_SERVER_ERROR),
            }
        }
        None => Err(StatusCode::NOT_FOUND),
    }
}

pub async fn delete_user(
    State(store): State<MemoryUserStore>,
    Path(id): Path<Uuid>,
) -> StatusCode {
    if store.delete_user(&id).await {
        StatusCode::NO_CONTENT
    } else {
        StatusCode::NOT_FOUND
    }
}

fn hash_password(password: &str) -> String {
    // 简单的密码哈希(生产环境应使用 bcrypt
    format!("hashed_{}", password)
}

学习重点:

  • Axum 提取器 (State, Path, Json)
  • HTTP 状态码处理
  • 错误处理和 Result 类型
  • 异步处理器函数

4.2 完整路由配置

更新 routes/mod.rs:

use axum::{
    Router,
    routing::{get, post, put, delete},
};
use crate::handlers;
use crate::storage::memory::MemoryUserStore;

pub fn create_routes(store: MemoryUserStore) -> Router {
    Router::new()
        .route("/", get(handlers::root))
        .route("/health", get(handlers::health_check))
        .nest("/api", api_routes())
        .with_state(store)
}

fn api_routes() -> Router<MemoryUserStore> {
    Router::new()
        .route("/users", get(handlers::user::list_users).post(handlers::user::create_user))
        .route("/users/:id", 
            get(handlers::user::get_user)
            .put(handlers::user::update_user)
            .delete(handlers::user::delete_user)
        )
}

学习重点:

  • 状态管理和依赖注入
  • RESTful 路由设计
  • HTTP 方法映射

阶段 5: 添加请求验证和错误处理

5.1 自定义错误类型

utils/errors.rs 实现:

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use serde_json::json;

#[derive(Debug)]
pub enum ApiError {
    ValidationError(String),
    NotFound(String),
    InternalError(String),
    Unauthorized,
}

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            ApiError::ValidationError(msg) => (StatusCode::BAD_REQUEST, msg),
            ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
            ApiError::InternalError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
            ApiError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized".to_string()),
        };

        let body = Json(json!({
            "error": error_message,
            "status": status.as_u16()
        }));

        (status, body).into_response()
    }
}

5.2 请求验证

添加验证逻辑:

use validator::{Validate, ValidationError};

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

// 在处理器中使用验证
pub async fn create_user(
    State(store): State<MemoryUserStore>,
    RequestJson(payload): RequestJson<CreateUserRequest>,
) -> Result<(StatusCode, Json<UserResponse>), ApiError> {
    // 验证请求数据
    payload.validate()
        .map_err(|e| ApiError::ValidationError(format!("Validation failed: {:?}", e)))?;
    
    // ... 其余逻辑
}

学习重点:

  • 自定义错误类型
  • IntoResponse trait 实现
  • 请求验证和数据校验
  • 错误传播 (? 操作符)

阶段 6: 实现基础的身份认证 (JWT)

6.1 JWT 中间件

middleware/auth.rs 实现:

use axum::{
    extract::{Request, State},
    http::{header, StatusCode},
    middleware::Next,
    response::Response,
};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    pub sub: String, // 用户ID
    pub exp: usize,  // 过期时间
}

pub async fn auth_middleware(
    mut req: Request,
    next: Next,
) -> Result<Response, StatusCode> {
    let auth_header = req.headers()
        .get(header::AUTHORIZATION)
        .and_then(|header| header.to_str().ok());

    let auth_header = if let Some(auth_header) = auth_header {
        auth_header
    } else {
        return Err(StatusCode::UNAUTHORIZED);
    };

    if let Some(token) = auth_header.strip_prefix("Bearer ") {
        match verify_jwt(token) {
            Ok(claims) => {
                req.extensions_mut().insert(claims);
                Ok(next.run(req).await)
            }
            Err(_) => Err(StatusCode::UNAUTHORIZED),
        }
    } else {
        Err(StatusCode::UNAUTHORIZED)
    }
}

fn verify_jwt(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
    let key = DecodingKey::from_secret("secret".as_ref());
    let validation = Validation::default();
    
    decode::<Claims>(token, &key, &validation)
        .map(|data| data.claims)
}

pub fn create_jwt(user_id: &str) -> Result<String, jsonwebtoken::errors::Error> {
    let expiration = chrono::Utc::now()
        .checked_add_signed(chrono::Duration::hours(24))
        .expect("valid timestamp")
        .timestamp() as usize;

    let claims = Claims {
        sub: user_id.to_string(),
        exp: expiration,
    };

    let key = EncodingKey::from_secret("secret".as_ref());
    encode(&Header::default(), &claims, &key)
}

6.2 登录端点

添加认证处理器:

#[derive(Deserialize)]
pub struct LoginRequest {
    pub username: String,
    pub password: String,
}

#[derive(Serialize)]
pub struct LoginResponse {
    pub token: String,
    pub user: UserResponse,
}

pub async fn login(
    State(store): State<MemoryUserStore>,
    RequestJson(payload): RequestJson<LoginRequest>,
) -> Result<Json<LoginResponse>, ApiError> {
    // 查找用户(简化版本,实际应该按用户名查找)
    let users = store.list_users().await;
    let user = users.into_iter()
        .find(|u| u.username == payload.username)
        .ok_or_else(|| ApiError::NotFound("User not found".to_string()))?;

    // 验证密码(简化版本)
    if user.password_hash != format!("hashed_{}", payload.password) {
        return Err(ApiError::Unauthorized);
    }

    // 生成 JWT
    let token = create_jwt(&user.id.to_string())
        .map_err(|_| ApiError::InternalError("Failed to create token".to_string()))?;

    Ok(Json(LoginResponse {
        token,
        user: user.into(),
    }))
}

学习重点:

  • JWT 概念和实现
  • 中间件设计模式
  • 请求扩展和状态传递
  • 身份认证流程

阶段 7: 添加 API 文档和测试用例

7.1 集成测试

tests/api_tests.rs 实现:

use reqwest;
use serde_json::json;
use tokio;

#[tokio::test]
async fn test_create_user() {
    let client = reqwest::Client::new();
    
    let user_data = json!({
        "username": "testuser",
        "email": "test@example.com",
        "password": "password123"
    });

    let response = client
        .post("http://localhost:3000/api/users")
        .json(&user_data)
        .send()
        .await
        .expect("Failed to send request");

    assert_eq!(response.status(), 201);
    
    let user: serde_json::Value = response.json().await.expect("Failed to parse JSON");
    assert_eq!(user["username"], "testuser");
    assert_eq!(user["email"], "test@example.com");
}

#[tokio::test]
async fn test_get_user() {
    // 首先创建用户,然后获取
    // ... 测试逻辑
}

7.2 API 文档

docs/api.md:

# User API Documentation

## Authentication
All protected endpoints require a Bearer token in the Authorization header:

Authorization: Bearer <jwt_token>


## Endpoints

### POST /api/auth/login
Login with username and password.

**Request Body:**
```json
{
  "username": "string",
  "password": "string"
}

Response:

{
  "token": "jwt_token_string",
  "user": {
    "id": "uuid",
    "username": "string",
    "email": "string",
    "created_at": "timestamp"
  }
}

GET /api/users

Get all users (requires authentication).

Response:

[
  {
    "id": "uuid",
    "username": "string",
    "email": "string",
    "created_at": "timestamp"
  }
]

**学习重点**:
- 集成测试编写
- HTTP 客户端测试
- API 文档编写规范
- 测试驱动开发

---

## 后续阶段概览

### 阶段 8-12: 高级功能
- **数据库集成**: SQLite/PostgreSQL 集成ORM 使用
- **高级功能**: 分页、搜索、过滤、排序
- **生产准备**: 配置管理、日志记录、错误监控
- **部署**: Docker 容器化、环境配置

每个阶段都建立在前一个阶段的基础上,确保学习的连续性和实用性。通过这种渐进式的方法,您将全面掌握 Rust web 开发的各个方面。