- 创建 Cargo.toml 配置文件,包含所有必要依赖 - 建立完整的项目模块结构(config, models, handlers, routes, services, storage, middleware, utils) - 实现用户数据模型和内存存储 - 创建基础的 HTTP 处理器和路由配置 - 添加错误处理和 JWT 认证中间件 - 配置环境变量和日志系统 - 创建项目文档和学习指南 - 服务器可以成功编译和启动
719 lines
16 KiB
Markdown
719 lines
16 KiB
Markdown
# Rust REST API Server - 详细实现指南
|
||
|
||
## 阶段 1: 项目初始化和基础设置
|
||
|
||
### 1.1 创建项目和基础配置
|
||
|
||
**目标**: 建立 Rust 项目基础结构和依赖管理
|
||
|
||
**具体步骤**:
|
||
1. 使用 `cargo new rust-user-api --bin` 创建新项目
|
||
2. 配置 `Cargo.toml` 文件,添加必要依赖
|
||
3. 创建基础目录结构
|
||
4. 设置开发环境配置
|
||
|
||
**Cargo.toml 配置**:
|
||
```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 实现**:
|
||
```rust
|
||
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 实现**:
|
||
```rust
|
||
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 实现**:
|
||
```rust
|
||
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 实现**:
|
||
```rust
|
||
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 实现**:
|
||
```rust
|
||
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**:
|
||
```rust
|
||
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 实现**:
|
||
```rust
|
||
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 请求验证
|
||
|
||
**添加验证逻辑**:
|
||
```rust
|
||
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 实现**:
|
||
```rust
|
||
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 登录端点
|
||
|
||
**添加认证处理器**:
|
||
```rust
|
||
#[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 实现**:
|
||
```rust
|
||
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**:
|
||
```markdown
|
||
# 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:**
|
||
```json
|
||
{
|
||
"token": "jwt_token_string",
|
||
"user": {
|
||
"id": "uuid",
|
||
"username": "string",
|
||
"email": "string",
|
||
"created_at": "timestamp"
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET /api/users
|
||
Get all users (requires authentication).
|
||
|
||
**Response:**
|
||
```json
|
||
[
|
||
{
|
||
"id": "uuid",
|
||
"username": "string",
|
||
"email": "string",
|
||
"created_at": "timestamp"
|
||
}
|
||
]
|
||
```
|
||
```
|
||
|
||
**学习重点**:
|
||
- 集成测试编写
|
||
- HTTP 客户端测试
|
||
- API 文档编写规范
|
||
- 测试驱动开发
|
||
|
||
---
|
||
|
||
## 后续阶段概览
|
||
|
||
### 阶段 8-12: 高级功能
|
||
- **数据库集成**: SQLite/PostgreSQL 集成,ORM 使用
|
||
- **高级功能**: 分页、搜索、过滤、排序
|
||
- **生产准备**: 配置管理、日志记录、错误监控
|
||
- **部署**: Docker 容器化、环境配置
|
||
|
||
每个阶段都建立在前一个阶段的基础上,确保学习的连续性和实用性。通过这种渐进式的方法,您将全面掌握 Rust web 开发的各个方面。 |