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

- 创建 Cargo.toml 配置文件,包含所有必要依赖
- 建立完整的项目模块结构(config, models, handlers, routes, services, storage, middleware, utils)
- 实现用户数据模型和内存存储
- 创建基础的 HTTP 处理器和路由配置
- 添加错误处理和 JWT 认证中间件
- 配置环境变量和日志系统
- 创建项目文档和学习指南
- 服务器可以成功编译和启动
This commit is contained in:
2025-08-04 16:49:50 +08:00
commit 28afc7532f
24 changed files with 2170 additions and 0 deletions

45
src/config/mod.rs Normal file
View File

@@ -0,0 +1,45 @@
//! Configuration management module
//!
//! This module handles application configuration including
//! server settings, database connections, and environment variables.
use std::env;
#[derive(Debug, Clone)]
pub struct Config {
pub server_host: String,
pub server_port: u16,
pub jwt_secret: String,
pub database_url: Option<String>,
}
impl Config {
pub fn from_env() -> Self {
dotenv::dotenv().ok();
Self {
server_host: env::var("SERVER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
server_port: env::var("SERVER_PORT")
.unwrap_or_else(|_| "3000".to_string())
.parse()
.expect("SERVER_PORT must be a valid number"),
jwt_secret: env::var("JWT_SECRET").unwrap_or_else(|_| "your-secret-key".to_string()),
database_url: env::var("DATABASE_URL").ok(),
}
}
pub fn server_address(&self) -> String {
format!("{}:{}", self.server_host, self.server_port)
}
}
impl Default for Config {
fn default() -> Self {
Self {
server_host: "127.0.0.1".to_string(),
server_port: 3000,
jwt_secret: "your-secret-key".to_string(),
database_url: None,
}
}
}

22
src/handlers/mod.rs Normal file
View File

@@ -0,0 +1,22 @@
//! HTTP 请求处理器模块
pub mod user;
use axum::{response::Json, http::StatusCode};
use serde_json::{json, Value};
/// 根路径处理器
pub async fn root() -> Json<Value> {
Json(json!({
"message": "欢迎使用 Rust User API",
"version": "0.1.0"
}))
}
/// 健康检查处理器
pub async fn health_check() -> (StatusCode, Json<Value>) {
(StatusCode::OK, Json(json!({
"status": "healthy",
"timestamp": chrono::Utc::now()
})))
}

109
src/handlers/user.rs Normal file
View File

@@ -0,0 +1,109 @@
//! 用户相关的 HTTP 处理器
use axum::{
extract::{Path, State},
http::StatusCode,
response::Json,
Json as RequestJson,
};
use uuid::Uuid;
use chrono::Utc;
use validator::Validate;
use crate::models::user::{User, UserResponse, CreateUserRequest, UpdateUserRequest};
use crate::storage::memory::MemoryUserStore;
use crate::utils::errors::ApiError;
/// 创建用户
pub async fn create_user(
State(store): State<MemoryUserStore>,
RequestJson(payload): RequestJson<CreateUserRequest>,
) -> Result<(StatusCode, Json<UserResponse>), ApiError> {
// 验证请求数据
payload.validate()?;
// 检查用户名是否已存在
if store.get_user_by_username(&payload.username).await.is_some() {
return Err(ApiError::Conflict("用户名已存在".to_string()));
}
// 创建新用户
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(e) => Err(ApiError::InternalError(e)),
}
}
/// 获取单个用户
pub async fn get_user(
State(store): State<MemoryUserStore>,
Path(id): Path<Uuid>,
) -> Result<Json<UserResponse>, ApiError> {
match store.get_user(&id).await {
Some(user) => Ok(Json(user.into())),
None => Err(ApiError::NotFound("用户不存在".to_string())),
}
}
/// 获取所有用户
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>, ApiError> {
// 验证请求数据
payload.validate()?;
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(ApiError::InternalError("更新用户失败".to_string())),
}
}
None => Err(ApiError::NotFound("用户不存在".to_string())),
}
}
/// 删除用户
pub async fn delete_user(
State(store): State<MemoryUserStore>,
Path(id): Path<Uuid>,
) -> Result<StatusCode, ApiError> {
if store.delete_user(&id).await {
Ok(StatusCode::NO_CONTENT)
} else {
Err(ApiError::NotFound("用户不存在".to_string()))
}
}
/// 简单的密码哈希函数(生产环境应使用 bcrypt
fn hash_password(password: &str) -> String {
format!("hashed_{}", password)
}

18
src/lib.rs Normal file
View File

@@ -0,0 +1,18 @@
//! Rust User API - A REST API server for user management
//!
//! This library provides a complete REST API server implementation
//! with user management, authentication, and CRUD operations.
pub mod config;
pub mod handlers;
pub mod middleware;
pub mod models;
pub mod routes;
pub mod services;
pub mod storage;
pub mod utils;
// Re-export commonly used types
pub use models::user::{User, UserResponse, CreateUserRequest, UpdateUserRequest};
pub use storage::memory::MemoryUserStore;
pub use utils::errors::ApiError;

35
src/main.rs Normal file
View File

@@ -0,0 +1,35 @@
//! Rust User API 服务器主程序
use std::net::SocketAddr;
use tracing_subscriber;
use rust_user_api::{
config::Config,
routes::create_routes,
storage::memory::MemoryUserStore,
};
#[tokio::main]
async fn main() {
// 初始化日志
tracing_subscriber::fmt::init();
// 加载配置
let config = Config::from_env();
// 创建存储实例
let store = MemoryUserStore::new();
// 创建路由
let app = create_routes(store);
// 启动服务器
let addr: SocketAddr = config.server_address().parse()
.expect("无效的服务器地址");
println!("🚀 服务器启动在 http://{}", addr);
println!("📚 API 文档: http://{}/", addr);
println!("❤️ 健康检查: http://{}/health", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}

70
src/middleware/auth.rs Normal file
View File

@@ -0,0 +1,70 @@
//! JWT 认证中间件
use axum::{
extract::Request,
http::{header, StatusCode},
middleware::Next,
response::Response,
};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
/// JWT Claims
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
pub sub: String, // 用户ID
pub exp: usize, // 过期时间
}
/// JWT 认证中间件
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)
}
}
/// 验证 JWT token
fn verify_jwt(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let key = DecodingKey::from_secret("your-secret-key".as_ref());
let validation = Validation::default();
decode::<Claims>(token, &key, &validation)
.map(|data| data.claims)
}
/// 创建 JWT token
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("your-secret-key".as_ref());
encode(&Header::default(), &claims, &key)
}

5
src/middleware/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
//! 中间件模块
pub mod auth;
pub use auth::{auth_middleware, Claims};

5
src/models/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
//! 数据模型模块
pub mod user;
pub use user::{User, UserResponse, CreateUserRequest, UpdateUserRequest, LoginRequest, LoginResponse};

72
src/models/user.rs Normal file
View File

@@ -0,0 +1,72 @@
//! 用户数据模型
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use validator::Validate;
/// 用户实体
#[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, 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,
}
/// 更新用户请求
#[derive(Debug, Deserialize, Validate)]
pub struct UpdateUserRequest {
#[validate(length(min = 3, max = 50))]
pub username: Option<String>,
#[validate(email)]
pub email: Option<String>,
}
/// 登录请求
#[derive(Debug, Deserialize)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
/// 登录响应
#[derive(Debug, Serialize)]
pub struct LoginResponse {
pub token: String,
pub user: UserResponse,
}
/// 从 User 转换为 UserResponse
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,
}
}
}

31
src/routes/mod.rs Normal file
View File

@@ -0,0 +1,31 @@
//! 路由配置模块
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)
}
/// API 路由
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)
)
}

5
src/services/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
//! 业务逻辑服务模块
pub mod user_service;
pub use user_service::UserService;

View File

@@ -0,0 +1,30 @@
//! 用户业务逻辑服务
use crate::models::user::User;
use crate::storage::memory::MemoryUserStore;
use crate::utils::errors::ApiError;
/// 用户服务
pub struct UserService {
store: MemoryUserStore,
}
impl UserService {
pub fn new(store: MemoryUserStore) -> Self {
Self { store }
}
/// 验证用户凭据
pub async fn authenticate_user(&self, username: &str, password: &str) -> Result<User, ApiError> {
match self.store.get_user_by_username(username).await {
Some(user) => {
if user.password_hash == format!("hashed_{}", password) {
Ok(user)
} else {
Err(ApiError::Unauthorized)
}
}
None => Err(ApiError::NotFound("用户不存在".to_string())),
}
}
}

68
src/storage/memory.rs Normal file
View File

@@ -0,0 +1,68 @@
//! 内存存储实现
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)
}
/// 根据 ID 获取用户
pub async fn get_user(&self, id: &Uuid) -> Option<User> {
let users = self.users.read().unwrap();
users.get(id).cloned()
}
/// 根据用户名获取用户
pub async fn get_user_by_username(&self, username: &str) -> Option<User> {
let users = self.users.read().unwrap();
users.values().find(|u| u.username == username).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()
}
}
impl Default for MemoryUserStore {
fn default() -> Self {
Self::new()
}
}

5
src/storage/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
//! 数据存储模块
pub mod memory;
pub use memory::MemoryUserStore;

44
src/utils/errors.rs Normal file
View File

@@ -0,0 +1,44 @@
//! 错误处理模块
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde_json::json;
/// API 错误类型
#[derive(Debug)]
pub enum ApiError {
ValidationError(String),
NotFound(String),
InternalError(String),
Unauthorized,
Conflict(String),
}
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, "未授权".to_string()),
ApiError::Conflict(msg) => (StatusCode::CONFLICT, msg),
};
let body = Json(json!({
"error": error_message,
"status": status.as_u16()
}));
(status, body).into_response()
}
}
/// 从验证错误转换为 API 错误
impl From<validator::ValidationErrors> for ApiError {
fn from(errors: validator::ValidationErrors) -> Self {
ApiError::ValidationError(format!("验证失败: {:?}", errors))
}
}

5
src/utils/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
//! 工具函数模块
pub mod errors;
pub use errors::ApiError;