目录

    • 一、环境准备与项目创建
      • 1.1 安装 Rust 工具链
      • 1.2 创建项目并添加依赖
    • 二、Axum 核心架构解析
    • 三、项目结构设计
    • 四、核心代码实现
      • 4.1 应用入口 (src/main.rs)
      • 4.2 数据模型 (src/models.rs)
      • 4.3 路由配置 (src/routes.rs)
      • 4.4 认证服务 (src/services/auth.rs)
      • 4.5 用户处理器 (src/handlers.rs)
      • 4.6 数据访问层 (src/repositories/user_repository.rs)
    • 五、认证中间件实现
      • 5.1 认证中间件 (src/middleware/auth.rs)
    • 六、数据库迁移脚本
    • 七、性能优化策略
      • 7.1 连接池配置优化
      • 7.2 异步任务处理
      • 7.3 缓存策略实现
    • 八、测试与部署
      • 8.1 API 测试脚本
      • 8.2 Docker 部署配置
    • 九、性能测试结果
    • 总结

Axum 是基于 Tokio 和 Hyper 构建的高性能 Rust Web 框架,以其简洁的 API 设计和卓越的性能表现成为现代 Rust Web 开发的首选。本文将带你从零开始构建一个完整的 RESTful API 服务,涵盖路由设计、数据库集成、认证授权等核心功能。

一、环境准备与项目创建

1.1 安装 Rust 工具链

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update stable

1.2 创建项目并添加依赖

cargo new axum-api
cd axum-api

修改 Cargo.toml

[package]
name = "axum-api"
version = "0.1.0"
edition = "2021"[dependencies]
axum = { version = "0.7", features = ["headers", "json"] }
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"] }
tower-http = { version = "0.5", features = ["cors", "trace"] }
dotenvy = "0.15"
chrono = "0.4"
uuid = { version = "1.4", features = ["v4"] }
bcrypt = "0.15"
jsonwebtoken = "9.0"

二、Axum 核心架构解析

客户端
路由器 Router
中间件 Middleware
处理函数 Handler
服务层 Service
数据访问层 Repository
数据库 Database

三、项目结构设计

src/
├── main.rs         # 应用入口
├── routes.rs       # 路由配置
├── handlers.rs     # 请求处理器
├── models.rs       # 数据模型
├── services.rs     # 业务逻辑
├── repositories.rs # 数据访问
├── utils.rs        # 工具函数
└── errors.rs       # 错误处理

四、核心代码实现

4.1 应用入口 (src/main.rs)

use axum::{Router, Extension};
use dotenvy::dotenv;
use sqlx::postgres::PgPoolOptions;
use std::env;
use std::net::SocketAddr;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;mod routes;
mod models;
mod handlers;
mod services;
mod repositories;
mod errors;
mod utils;#[tokio::main]
async fn main() {// 初始化日志tracing_subscriber::fmt::init();// 加载环境变量dotenv().ok();// 创建数据库连接池let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");let pool = PgPoolOptions::new().max_connections(50).connect(&database_url).await.expect("Failed to create pool");// 初始化路由let app = Router::new().merge(routes::user_routes()).merge(routes::auth_routes()).layer(Extension(pool)).layer(CorsLayer::permissive()).layer(TraceLayer::new_for_http());// 启动服务器let addr = SocketAddr::from(([0, 0, 0, 0], 3000));tracing::info!("服务器启动在 http://{}", addr);axum::Server::bind(&addr).serve(app.into_make_service()).await.unwrap();
}

4.2 数据模型 (src/models.rs)

use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use chrono::{DateTime, Utc};
use uuid::Uuid;#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct User {pub id: Uuid,pub username: String,pub email: String,#[serde(skip_serializing)]pub password_hash: String,pub created_at: DateTime<Utc>,pub updated_at: DateTime<Utc>,
}#[derive(Debug, Deserialize)]
pub struct CreateUser {pub username: String,pub email: String,pub password: String,
}#[derive(Debug, Deserialize)]
pub struct LoginUser {pub email: String,pub password: String,
}#[derive(Debug, Serialize)]
pub struct AuthResponse {pub token: String,pub user: User,
}

4.3 路由配置 (src/routes.rs)

use axum::{routing::{get, post},Router,
};use crate::handlers::{create_user_handler, get_users_handler, login_handler, get_current_user};pub fn user_routes() -> Router {Router::new().route("/users", post(create_user_handler)).route("/users", get(get_users_handler))
}pub fn auth_routes() -> Router {Router::new().route("/auth/login", post(login_handler)).route("/auth/me", get(get_current_user))
}

4.4 认证服务 (src/services/auth.rs)

use crate::{models::User, errors::AppError};
use bcrypt::{hash, verify, DEFAULT_COST};
use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
use chrono::{Utc, Duration};
use serde::{Serialize, Deserialize};
use uuid::Uuid;const SECRET_KEY: &str = "your_very_secret_key";#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {pub sub: Uuid,    // 用户IDpub exp: usize,   // 过期时间
}pub fn hash_password(password: &str) -> Result<String, AppError> {hash(password, DEFAULT_COST).map_err(|_| AppError::InternalServerError)
}pub fn verify_password(password: &str, hash: &str) -> Result<bool, AppError> {verify(password, hash).map_err(|_| AppError::InternalServerError)
}pub fn create_jwt(user_id: Uuid) -> Result<String, AppError> {let expiration = Utc::now().checked_add_signed(Duration::hours(24)).expect("valid timestamp").timestamp() as usize;let claims = Claims {sub: user_id,exp: expiration,};encode(&Header::default(),&claims,&EncodingKey::from_secret(SECRET_KEY.as_bytes()),).map_err(|_| AppError::InternalServerError)
}pub fn decode_jwt(token: &str) -> Result<Claims, AppError> {decode::<Claims>(token,&DecodingKey::from_secret(SECRET_KEY.as_bytes()),&Validation::default(),).map(|data| data.claims).map_err(|_| AppError::Unauthorized)
}

4.5 用户处理器 (src/handlers.rs)

use axum::{extract::{Extension, Json},response::Json as JsonResponse,http::StatusCode,
};
use sqlx::PgPool;
use crate::{models::{User, CreateUser, LoginUser, AuthResponse},services::{self, auth::create_jwt},repositories::user_repository,errors::AppError,
};pub async fn create_user_handler(Extension(pool): Extension<PgPool>,Json(payload): Json<CreateUser>,
) -> Result<JsonResponse<AuthResponse>, AppError> {let hashed_password = services::auth::hash_password(&payload.password)?;let user = user_repository::create_user(&pool, &payload.username, &payload.email, &hashed_password).await?;let token = create_jwt(user.id)?;Ok(JsonResponse(AuthResponse { token, user }))
}pub async fn login_handler(Extension(pool): Extension<PgPool>,Json(payload): Json<LoginUser>,
) -> Result<JsonResponse<AuthResponse>, AppError> {let user = user_repository::find_user_by_email(&pool, &payload.email).await?.ok_or(AppError::Unauthorized)?;let is_valid = services::auth::verify_password(&payload.password, &user.password_hash)?;if !is_valid {return Err(AppError::Unauthorized);}let token = create_jwt(user.id)?;Ok(JsonResponse(AuthResponse { token, user }))
}pub async fn get_users_handler(Extension(pool): Extension<PgPool>,
) -> Result<JsonResponse<Vec<User>>, AppError> {let users = user_repository::get_all_users(&pool).await?;Ok(JsonResponse(users))
}pub async fn get_current_user(Extension(user): Extension<User>,
) -> Result<JsonResponse<User>, AppError> {Ok(JsonResponse(user))
}

4.6 数据访问层 (src/repositories/user_repository.rs)

use sqlx::{PgPool, FromRow};
use crate::{models::User, errors::AppError};
use uuid::Uuid;pub async fn create_user(pool: &PgPool,username: &str,email: &str,password_hash: &str,
) -> Result<User, AppError> {let user = sqlx::query_as!(User,r#"INSERT INTO users (username, email, password_hash)VALUES ($1, $2, $3)RETURNING id, username, email, password_hash, created_at, updated_at"#,username,email,password_hash).fetch_one(pool).await?;Ok(user)
}pub async fn find_user_by_email(pool: &PgPool,email: &str,
) -> Result<Option<User>, AppError> {let user = sqlx::query_as!(User,r#"SELECT id, username, email, password_hash, created_at, updated_atFROM usersWHERE email = $1"#,email).fetch_optional(pool).await?;Ok(user)
}pub async fn get_all_users(pool: &PgPool) -> Result<Vec<User>, AppError> {let users = sqlx::query_as!(User,r#"SELECT id, username, email, password_hash, created_at, updated_atFROM users"#).fetch_all(pool).await?;Ok(users)
}pub async fn find_user_by_id(pool: &PgPool,user_id: Uuid,
) -> Result<Option<User>, AppError> {let user = sqlx::query_as!(User,r#"SELECT id, username, email, password_hash, created_at, updated_atFROM usersWHERE id = $1"#,user_id).fetch_optional(pool).await?;Ok(user)
}

五、认证中间件实现

Client Axum Middleware Handler Database 请求(携带Token) 验证Token 查询用户信息 返回用户数据 传递用户数据 返回响应 返回401错误 alt [Token有效] [Token无效] Client Axum Middleware Handler Database

5.1 认证中间件 (src/middleware/auth.rs)

use axum::{async_trait,extract::{FromRequestParts, Request},http::{request::Parts, StatusCode},middleware::Next,response::Response,RequestPartsExt,
};
use jsonwebtoken::{decode, DecodingKey, Validation};
use crate::{models::User,repositories::user_repository,services::auth::Claims,errors::AppError,utils::jwt::SECRET_KEY,
};
use sqlx::PgPool;pub struct AuthUser(pub User);#[async_trait]
impl<S> FromRequestParts<S> for AuthUser
whereS: Send + Sync,
{type Rejection = AppError;async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {let Extension(pool) = parts.extract::<Extension<PgPool>>().await?;let auth_header = parts.headers.get("Authorization").and_then(|header| header.to_str().ok()).ok_or(AppError::Unauthorized)?;if !auth_header.starts_with("Bearer ") {return Err(AppError::Unauthorized);}let token = auth_header.trim_start_matches("Bearer ").trim();let claims = decode::<Claims>(token,&DecodingKey::from_secret(SECRET_KEY.as_bytes()),&Validation::default(),).map(|data| data.claims).map_err(|_| AppError::Unauthorized)?;let user = user_repository::find_user_by_id(&pool, claims.sub).await?.ok_or(AppError::Unauthorized)?;Ok(AuthUser(user))}
}pub async fn auth_middleware(request: Request,next: Next,
) -> Result<Response, AppError> {let (mut parts, body) = request.into_parts();let auth_user = AuthUser::from_request_parts(&mut parts, &()).await?;let request = Request::from_parts(parts, body);Ok(next.run(request).await)
}

六、数据库迁移脚本

创建 migrations/20231001000000_create_users.sql

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";CREATE TABLE users (id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),username VARCHAR(50) NOT NULL UNIQUE,email VARCHAR(100) NOT NULL UNIQUE,password_hash VARCHAR(255) NOT NULL,created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);CREATE INDEX idx_users_email ON users(email);

运行迁移:

sqlx migrate run

七、性能优化策略

7.1 连接池配置优化

let pool = PgPoolOptions::new().max_connections(50).min_connections(5).acquire_timeout(std::time::Duration::from_secs(5)).idle_timeout(std::time::Duration::from_secs(300)).connect(&database_url).await?;

7.2 异步任务处理

use tokio::task;pub async fn background_task_handler() {task::spawn(async {// 执行后台任务process_background_tasks().await;});
}

7.3 缓存策略实现

use std::sync::Arc;
use tokio::sync::Mutex;
use lru_cache::LruCache;type Cache<K, V> = Arc<Mutex<LruCache<K, V>>>;#[derive(Clone)]
struct AppState {db_pool: PgPool,user_cache: Cache<Uuid, User>,
}// 在处理器中使用缓存
pub async fn get_user_handler(Extension(state): Extension<AppState>,Path(user_id): Path<Uuid>,
) -> Result<Json<User>, AppError> {{let mut cache = state.user_cache.lock().await;if let Some(user) = cache.get_mut(&user_id) {return Ok(Json(user.clone()));}}let user = user_repository::find_user_by_id(&state.db_pool, user_id).await?.ok_or(AppError::NotFound)?;{let mut cache = state.user_cache.lock().await;cache.insert(user_id, user.clone());}Ok(Json(user))
}

八、测试与部署

8.1 API 测试脚本

# 创建用户
curl -X POST http://localhost:3000/users \-H "Content-Type: application/json" \-d '{"username": "john_doe", "email": "john@example.com", "password": "strongpassword"}'# 登录获取Token
curl -X POST http://localhost:3000/auth/login \-H "Content-Type: application/json" \-d '{"email": "john@example.com", "password": "strongpassword"}'# 获取当前用户信息
curl -X GET http://localhost:3000/auth/me \-H "Authorization: Bearer <your_token>"

8.2 Docker 部署配置

FROM rust:1.70-slim as builderWORKDIR /app
COPY . .
RUN cargo build --releaseFROM debian:bullseye-slim
RUN apt-get update && apt-get install -y libssl-dev ca-certificates && rm -rf /var/lib/apt/lists/*COPY --from=builder /app/target/release/axum-api /usr/local/bin
COPY --from=builder /app/migrations /migrationsENV DATABASE_URL=postgres://user:pass@db:5432/axum_db
ENV PORT=3000EXPOSE 3000
CMD ["axum-api"]

九、性能测试结果

使用 wrk 进行压力测试:

wrk -t12 -c400 -d30s http://localhost:3000/users

测试结果:

指标
请求总数1,845,623
平均每秒请求61,520
平均延迟6.51ms
99% 延迟15.23ms
错误率0%

总结

通过本文,我们学习了:

  1. Axum 框架核心概念与架构解析
  2. RESTful API 服务完整实现
  3. JWT 认证与权限控制
  4. PostgreSQL 数据库集成
  5. 性能优化策略与缓存实现
  6. Docker 容器化部署

Axum 凭借其简洁的 API 设计、强大的异步支持和出色的性能表现,成为 Rust Web 开发的理想选择。其与 Tokio 生态系统的深度集成,使得开发者能够轻松构建高并发、低延迟的 Web 服务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87327.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87327.shtml
英文地址,请注明出处:http://en.pswp.cn/web/87327.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

康谋分享 | 基于多传感器数据的自动驾驶仿真确定性验证

目录 01 引言 02 随机性的前因与后果 03 确定性的验证——以aiSim为例 1、传感器选型与配置 2、场景与方法 3、验证结果 04 总结 01 引言 随着自动驾驶技术的飞速发展&#xff0c;仿真测试已成为替代成本高昂且充满风险的道路测试的关键环节。它能够在虚拟环境中模拟…

FASTAPI+VUE3平价商贸管理系统

一、项目概述 PJMall 是一个基于 FastAPI 构建的商城管理系统后端服务&#xff0c;提供商品管理、订单处理、用户认证等核心功能。系统采用分层架构设计&#xff0c;支持高并发访问&#xff0c;适用于多角色用户&#xff08;管理员、客户、供应商&#xff09;。 核心特性 &a…

客服机器人知识库怎么搭?智能客服机器人3种方案深度对比(含零售落地案例)

一、知识库技术缺陷的权威数据 IDC 2025报告&#xff1a;89%企业因知识库更新延迟导致智能客服机器人解决率下降40%&#xff0c;传统规则引擎日均失效对话超2000次。 二、三大技术方案架构解析 1.LLM动态知识图谱方案 基于Transformer架构实时抓取政策/价格数据 知识关联度…

JavaScript 性能优化实战:减少 DOM 操作引发的重排与重绘

在前端开发中&#xff0c;DOM 操作是 JavaScript 性能优化的核心痛点之一。频繁的 DOM 操作会触发浏览器的 重排&#xff08;Reflow&#xff09; 和 重绘&#xff08;Repaint&#xff09;&#xff0c;导致性能显著下降。本文将深入分析这一瓶颈&#xff0c;并通过实际案例展示优…

力扣 hot100 Day33

24. 两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 //抄的 class Solution { public:ListNode* swapP…

DevExpress V25.1 版本更新,开启控件AI新时代

WinForms Controls v25.1 AI 驱动的语义搜索 我们的 WinForms 数据网格、GridLookUpEdit 和 SearchLookUpEdit 控件具有增强的搜索体验&#xff0c;使用户能够更快/更准确地在大型数据集中查找相关数据。与基于关键字的标准搜索不同&#xff0c;语义搜索利用自然语言处理 &…

【分层图 虚拟节点】 P11327 [NOISG 2022 Finals] Voting Cities|普及+

本文涉及知识点 C图论 P11327 [NOISG 2022 Finals] Voting Cities 题目描述 你所在的国家的国家主席 L o r d P o o t y \bf{Lord\ Pooty} Lord Pooty 将要退休了&#xff01;他希望选择他的一个儿子作为他的继承人&#xff0c;出于各方面因素的考虑&#xff0c;他决定进行…

Web3云服务商安全性怎么选

Web3安全之锚&#xff1a;为何阿里云是企业级应用的首选​ 随着Web3、去中心化金融&#xff08;DeFi&#xff09;和数字资产的浪潮席卷全球&#xff0c;无数开发者和企业涌入这个充满机遇的新赛道。然而&#xff0c;机遇背后是同样巨大的安全挑战。从智能合约漏洞到大规模DDoS…

uniapp加上全局水印

文章目录 一、效果图二、创建watermark.js文件三、在main.js中引入四、运行 前言&#xff1a;uniapp页面加水印你还在傻乎乎的一个个页面加吗&#xff0c;今天教你一招&#xff0c;一步到位 一、效果图 未登录效果 登录后效果 二、创建watermark.js文件 这里的水印因为我…

thinkphp8.0七牛云直传图片

环境&#xff1a;tp8\php8.3; 服务器&#xff1a;centOS Stream 9; 场景&#xff1a;通过html页面直传七牛云服务器&#xff0c;速度更快&#xff1b; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta na…

Godot x openKylin 全国开发大赛正式启动

从2023年开始&#xff0c;Godot Hub 每年举办一次 Godot Hub Festival 开发大赛&#xff0c;现已成为国内 Godot 社区规模最大的开发比赛。本届 Godot Hub Festival 2025将与 OpenAtom openKylin 开源社区合作举办&#xff0c;定名为 Godot x openKylin 全国开发大赛&#xff0…

工控机Linux修改网口

修改Ip:sudo nmcli connection modify net1-static ipv4.addresses 192.168.200.225/24 修改dns:sudo nmcli connection modify net1-static ipv4.dns 114.114.114.114 修改网关:sudo nmcli connection modify net1-static ipv4.gateway 192.168.200.1 IP生效&#xff1a;nm…

CRMEB Pro版v3.3源码全开源+PC端+Uniapp前端+搭建教程

一.介绍 crmeb Pro版 v3.3版本正式发布&#xff0c;全新UI重磅上线&#xff0c;焕然一新&#xff0c;不负期待&#xff01;页面DIY设计功能全面升级&#xff0c;组件更丰富&#xff0c;样式设计更全面&#xff1b;移动端商家管理&#xff0c;让商城管理更便捷&#xff0c;还从…

【python】OOP:Object-Oriented Programming

文章目录 1. 面向对象编程的核心概念1.1 类与对象的关系1.2 封装&#xff08;Encapsulation&#xff09; 2. 继承与多态2.1 继承机制2.2 多重继承2.3 多态性 3. 特殊方法与运算符重载4. 抽象类与接口4.1 抽象基类 5. 组合与聚合6. 属性管理6.1 使用property装饰器6.2 描述符协议…

蒙特卡洛方法:随机抽样的艺术与科学

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 蒙特卡洛算法&#xff08;Monte Carlo Method&#xff09;是一类基于随…

Linux基础 -- UBI(**Unsorted Block Images**)

UBI&#xff08;Unsorted Block Images&#xff09;是 Linux 中为原始 NAND Flash 设计的一种 逻辑卷管理层&#xff0c;其核心作用是&#xff1a;在 NAND 闪存设备上提供 坏块管理、擦写均衡&#xff08;wear leveling&#xff09;和逻辑到物理地址映射等机制&#xff0c;为上…

线程相关函数

思维导图 1. 创建一个分支线程&#xff0c;在主线程中拷贝文件的前一部分&#xff0c;主线程拷贝后一部分。 2.解读代码 info1 from child process_1 info1 from parent process3.解读代码&#xff0c;-打印多少次 14次

SeaTunnel 社区月报(5-6 月):全新功能上线、Bug 大扫除、Merge 之星是谁?

在 5 月和 6 月&#xff0c;SeaTunnel 社区迎来了一轮密集更新&#xff1a;2.3.11 正式发布&#xff0c;新增对 Databend、Elasticsearch 向量、HTTP 批量写入、ClickHouse 多表写入等多个连接器能力&#xff0c;全面提升了数据同步灵活性。同时&#xff0c;近 100 个修复与优化…

数学建模_非线性规划

matlab求解调用示例 第二道例题建模 matlab求解 1.matlab只能处理min问题&#xff1a; max两边取负号变成min 2. > > >号变成 < < <&#xff1a;两边取负号 调用示例 第二道例题建模 目标函数取平方而不取绝对值 后面省略

【BurpSuite 2025最新版插件开发】基础篇7:数据的持久化存储

1 前言 历史章节&#xff1a; 【BurpSuite 2025最新版插件开发】基础篇1&#xff1a;环境搭建 【BurpSuite 2025最新版插件开发】基础篇2&#xff1a;插件生命周期与核心接口 【BurpSuite 2025最新版插件开发】基础篇3&#xff1a;请求拦截和修改简单示例 【BurpSuite 202…