- 创建 Cargo.toml 配置文件,包含所有必要依赖 - 建立完整的项目模块结构(config, models, handlers, routes, services, storage, middleware, utils) - 实现用户数据模型和内存存储 - 创建基础的 HTTP 处理器和路由配置 - 添加错误处理和 JWT 认证中间件 - 配置环境变量和日志系统 - 创建项目文档和学习指南 - 服务器可以成功编译和启动
16 KiB
16 KiB
Rust REST API Server - 详细实现指南
阶段 1: 项目初始化和基础设置
1.1 创建项目和基础配置
目标: 建立 Rust 项目基础结构和依赖管理
具体步骤:
- 使用
cargo new rust-user-api --bin
创建新项目 - 配置
Cargo.toml
文件,添加必要依赖 - 创建基础目录结构
- 设置开发环境配置
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 开发的各个方面。