第一步:创建项目
bash
cargo new rest-api
cd rest-apiCargo.toml
toml
[package]
name = "rest-api"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = { version = "0.7", features = ["macros"] }
tokio = { version = "1", features = ["full"] }
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "trace"] }
# 数据库
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "uuid", "chrono"] }
# 序列化
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# 认证
jsonwebtoken = "9"
# 工具
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1"
thiserror = "1"
tracing = "0.1"
tracing-subscriber = "0.3"
dotenvy = "0.15"
bcrypt = "0.15"第二步:配置模块
src/config.rs
rust
▶ Runuse serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct Config {
pub database_url: String,
pub jwt_secret: String,
pub jwt_expiration: u64,
pub server_host: String,
pub server_port: u16,
}
impl Config {
pub fn from_env() -> anyhow::Result<Self> {
dotenvy::dotenv().ok();
Ok(Self {
database_url: std::env::var("DATABASE_URL")?,
jwt_secret: std::env::var("JWT_SECRET")?,
jwt_expiration: std::env::var("JWT_EXPIRATION")
.unwrap_or("3600".to_string())
.parse()?,
server_host: std::env::var("SERVER_HOST")
.unwrap_or("127.0.0.1".to_string()),
server_port: std::env::var("SERVER_PORT")
.unwrap_or("3000".to_string())
.parse()?,
})
}
}.env
env
DATABASE_URL=postgres://user:password@localhost/rest_api
JWT_SECRET=your-secret-key
JWT_EXPIRATION=3600
SERVER_HOST=127.0.0.1
SERVER_PORT=3000第三步:数据库模块
src/db.rs
rust
▶ Runuse sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
pub async fn create_pool(database_url: &str) -> anyhow::Result<PgPool> {
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(database_url)
.await?;
Ok(pool)
}migrations/001_users.sql
sql
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
username VARCHAR(100) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);第四步:用户模型
src/models/user.rs
rust
▶ Runuse chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
/// 用户数据库模型
#[derive(Debug, FromRow, Serialize, Deserialize)]
pub struct User {
pub id: Uuid,
pub email: String,
pub password_hash: String,
pub username: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
/// 创建用户请求
#[derive(Debug, Deserialize)]
pub struct CreateUser {
pub email: String,
pub password: String,
pub username: String,
}
/// 更新用户请求
#[derive(Debug, Deserialize)]
pub struct UpdateUser {
pub username: Option<String>,
pub email: Option<String>,
}
/// 用户响应(不含密码)
#[derive(Debug, Serialize)]
pub struct UserResponse {
pub id: Uuid,
pub email: String,
pub username: String,
pub created_at: DateTime<Utc>,
}
impl From<User> for UserResponse {
fn from(user: User) -> Self {
Self {
id: user.id,
email: user.email,
username: user.username,
created_at: user.created_at,
}
}
}第五步:认证模型
src/models/auth.rs
rust
▶ Runuse serde::{Deserialize, Serialize};
/// 登录请求
#[derive(Debug, Deserialize)]
pub struct LoginRequest {
pub email: String,
pub password: String,
}
/// 登录响应
#[derive(Debug, Serialize)]
pub struct LoginResponse {
pub token: String,
pub user: crate::models::user::UserResponse,
}
/// JWT Claims
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String, // 用户 ID
pub email: String,
pub exp: usize, // 过期时间
pub iat: usize, // 签发时间
}