🦀 第22课:Web服务(Axum)

实战项目 第22/25课

Axum是Tokio团队开发的Web框架,以类型安全和高性能著称。本课将构建一个完整的RESTful API服务。

🌐 Axum基础

// Cargo.toml:
// [dependencies]
// axum = "0.8"
// tokio = { version = "1", features = ["full"] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"

// 概念演示(不依赖tokio运行时)
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct Todo {
    id: u64,
    title: String,
    completed: bool,
}

struct TodoStore {
    todos: HashMap<u64, Todo>,
    next_id: u64,
}

impl TodoStore {
    fn new() -> Self { TodoStore { todos: HashMap::new(), next_id: 1 } }
    
    fn create(&mut self, title: String) -> &Todo {
        let id = self.next_id;
        self.next_id += 1;
        let todo = Todo { id, title, completed: false };
        self.todos.insert(id, todo);
        self.todos.get(&id).unwrap()
    }
    
    fn get(&self, id: u64) -> Option<&Todo> { self.todos.get(&id) }
    
    fn list(&self) -> Vec<&Todo> { self.todos.values().collect() }
    
    fn toggle(&mut self, id: u64) -> Option<&Todo> {
        if let Some(todo) = self.todos.get_mut(&id) {
            todo.completed = !todo.completed;
            Some(self.todos.get(&id).unwrap())
        } else { None }
    }
    
    fn delete(&mut self, id: u64) -> bool { self.todos.remove(&id).is_some() }
}

fn main() {
    let mut store = TodoStore::new();
    
    // 模拟API请求
    let t1 = store.create("学习Rust".to_string());
    let t2 = store.create("构建Web服务".to_string());
    let t3 = store.create("写文档".to_string());
    
    println!("📋 所有TODO:");
    for todo in store.list() {
        println!("  [{}] {} (id={})", if todo.completed {"✅"} else {"⬜"}, todo.title, todo.id);
    }
    
    // 切换完成状态
    store.toggle(1);
    println!("\n切换后:");
    for todo in store.list() {
        println!("  [{}] {}", if todo.completed {"✅"} else {"⬜"}, todo.title);
    }
    
    // 删除
    store.delete(3);
    println!("\n删除后剩余{}项", store.list().len());
    
    // Axum路由概念
    println!("\n=== Axum路由结构 ===");
    println!("GET    /api/todos       → 列出所有");
    println!("POST   /api/todos       → 创建新项");
    println!("GET    /api/todos/:id   → 获取单个");
    println!("PUT    /api/todos/:id   → 更新");
    println!("DELETE /api/todos/:id   → 删除");
}
📋 所有TODO: [⬜] 学习Rust (id=1) [⬜] 构建Web服务 (id=2) [⬜] 写文档 (id=3) 切换后: [✅] 学习Rust [⬜] 构建Web服务 [⬜] 写文档 删除后剩余2项 === Axum路由结构 === GET /api/todos → 列出所有 POST /api/todos → 创建新项 GET /api/todos/:id → 获取单个 PUT /api/todos/:id → 更新 DELETE /api/todos/:id → 删除

✅ 验证通过

📝 练习

练习1:启动真实Axum

创建Axum项目,实现GET /和POST /echo路由。

练习2:中间件

实现请求日志中间件,记录每个请求的方法、路径和耗时。

🏆 本课成就

🔧 Web服务架构

// REST API设计原则
// GET /api/tasks - 列出
// POST /api/tasks - 创建
// PUT /api/tasks/:id - 更新
// DELETE /api/tasks/:id - 删除

// 中间件概念
fn logging_middleware(method: &str, path: &str, status: u16, elapsed_ms: u64) {
    println!("[{}] {} → {} ({}ms)", method, path, status, elapsed_ms);
}

fn main() {
    logging_middleware("GET", "/api/tasks", 200, 5);
    logging_middleware("POST", "/api/tasks", 201, 12);
    logging_middleware("GET", "/api/tasks/999", 404, 2);
    
    println!("
Axum中间件:");
    println!("  use axum::middleware;");
    println!("  let app = Router::new()");
    println!("      .route("/api", get(handler))");
    println!("      .layer(middleware::from_fn(log_middleware));");
}
[GET] /api/tasks → 200 (5ms) [POST] /api/tasks → 201 (12ms) [GET] /api/tasks/999 → 404 (2ms)

✅ 验证通过

📊 Web服务部署

fn main() {
    println!("Rust Web服务部署选项:");
    println!();
    println!("1. 独立二进制部署:");
    println!("   cargo build --release");
    println!("   ./target/release/myserver");
    println!("   // 单文件部署,无需运行时!");
    println!();
    println!("2. Docker部署:");
    println!("   FROM rust:1.95 AS builder");
    println!("   WORKDIR /app");
    println!("   COPY . .");
    println!("   RUN cargo build --release");
    println!("   ");
    println!("   FROM debian:bookworm-slim");
    println!("   COPY --from=builder /app/target/release/myserver /");
    println!("   CMD ["./myserver"]");
    println!();
    println!("3. Systemd服务:");
    println!("   [Unit]");
    println!("   Description=My Rust Server");
    println!("   [Service]");
    println!("   ExecStart=/usr/local/bin/myserver");
    println!("   Restart=always");
    println!();
    println!("性能基准:");
    println!("  Axum:   ~300k req/s (hello world)");
    println!("  Actix:  ~400k req/s (hello world)");
    println!("  Node:   ~50k req/s (hello world)");
    println!("  Go:     ~200k req/s (hello world)");
}

🏗️ 综合实战:REST API模拟

use std::collections::HashMap;

#[derive(Debug, Clone)]
struct Task { id: u64, title: String, done: bool }

struct ApiServer { tasks: HashMap, next_id: u64 }
impl ApiServer {
    fn new() -> Self { ApiServer { tasks: HashMap::new(), next_id: 1 } }
    fn list(&self) -> Vec<&Task> { self.tasks.values().collect() }
    fn create(&mut self, title: &str) -> &Task {
        let id = self.next_id; self.next_id += 1;
        let t = Task { id, title: title.into(), done: false };
        self.tasks.insert(id, t); self.tasks.get(&id).unwrap()
    }
    fn get(&self, id: u64) -> Option<&Task> { self.tasks.get(&id) }
    fn update(&mut self, id: u64, title: Option<&str>, done: Option) -> Option<&Task> {
        let t = self.tasks.get_mut(&id)?;
        if let Some(s) = title { t.title = s.into(); }
        if let Some(d) = done { t.done = d; }
        Some(self.tasks.get(&id).unwrap())
    }
    fn delete(&mut self, id: u64) -> bool { self.tasks.remove(&id).is_some() }
}

fn main() {
    let mut api = ApiServer::new();
    api.create("学习Axum"); api.create("写API"); api.create("部署上线");
    api.update(1, None, Some(true));
    
    println!("GET /api/tasks:");
    for t in api.list() { println!("  [{}] {} (id={})", if t.done{"✅"}else{"⬜"}, t.title, t.id); }
    
    println!("
GET /api/tasks/2:");
    if let Some(t) = api.get(2) { println!("  {} (done={})", t.title, t.done); }
    
    api.delete(3);
    println!("
DELETE /api/tasks/3 后剩余{}项", api.list().len());
}
GET /api/tasks: [✅] 学习Axum (id=1) [⬜] 写API (id=2) [⬜] 部署上线 (id=3) GET /api/tasks/2: 写API (done=false) DELETE /api/tasks/3 后剩余2项

✅ 验证通过

📋 Web服务部署清单

生产部署要点

🔧 Axum完整项目模板

// Cargo.toml:
// axum = "0.8"
// tokio = { version = "1", features = ["full"] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
// tower-http = { version = "0.6", features = ["cors", "trace"] }
// tracing = "0.1"
// tracing-subscriber = "0.3"
// sqlx = { version = "0.8", features = ["postgres", "runtime-tokio"] }
//
// 项目结构:
// src/
// ├── main.rs          - 入口+路由
// ├── handlers/        - 请求处理器
// ├── models/          - 数据模型
// ├── services/        - 业务逻辑
// ├── repositories/    - 数据访问
// └── config.rs        - 配置管理

fn main() {
    println!("Axum项目架构:");
    println!("  src/main.rs     - 服务器启动+路由");
    println!("  src/handlers/   - HTTP处理器");
    println!("  src/models/     - 数据模型(DTO)");
    println!("  src/services/   - 业务逻辑层");
    println!("  src/repositories/ - 数据访问层");
    println!("  src/config.rs   - 配置管理");
    println!("  src/middleware/  - 中间件");
    println!("  src/errors.rs   - 错误类型");
    
    println!("
性能数据:");
    println!("  单机: 30万+ req/s (hello world)");
    println!("  延迟: <100μs (本地)");
    println!("  内存: <10MB (空闲)");
}

🏗️ Web实战:中间件系统

use std::collections::HashMap;
use std::time::Instant;

struct Request { method: String, path: String, headers: HashMap, body: Option }
struct Response { status: u16, body: String }
impl Response { fn ok(body: &str) -> Self { Response { status: 200, body: body.into() } } fn not_found() -> Self { Response { status: 404, body: "Not Found".into() } } fn json(body: &str) -> Self { Response { status: 200, body: body.into() } } }

type Handler = fn(Request) -> Response;
type Middleware = fn(&Request, &Response, u64);

struct App {
    routes: HashMap,
    middlewares: Vec,
}

impl App {
    fn new() -> Self { App { routes: HashMap::new(), middlewares: Vec::new() } }
    fn route(mut self, key: &str, handler: Handler) -> Self { self.routes.insert(key.into(), handler); self }
    fn middleware(mut self, m: Middleware) -> Self { self.middlewares.push(m); self }
    fn handle(&self, req: Request) -> Response {
        let key = format!("{} {}", req.method, req.path);
        let start = Instant::now();
        let resp = self.routes.get(&key).map(|h| h(req)).unwrap_or_else(Response::not_found);
        let elapsed = start.elapsed().as_millis() as u64;
        for m in &self.middlewares { m(&Request{method:String::new(),path:String::new(),headers:HashMap::new(),body:None}, &resp, elapsed); }
        resp
    }
}

fn log_middleware(_req: &Request, resp: &Response, ms: u64) { println!("[LOG] {} ({}ms)", resp.status, ms); }
fn index(_req: Request) -> Response { Response::ok("Welcome!") }
fn api_health(_req: Request) -> Response { Response::json("{"status":"ok"}") }

fn main() {
    let app = App::new()
        .route("GET /", index)
        .route("GET /api/health", api_health)
        .middleware(log_middleware);
    
    println!("GET / → {}", app.handle(Request{method:"GET".into(),path:"/".into(),headers:HashMap::new(),body:None}).status);
    println!("GET /api/health → {}", app.handle(Request{method:"GET".into(),path:"/api/health".into(),headers:HashMap::new(),body:None}).body);
}
[LOG] 200 (0ms) GET / → 200 [LOG] 200 (0ms) GET /api/health → {"status":"ok"}

✅ 验证通过

💡 本课要点回顾

Rust的设计哲学贯穿始终:安全、并发、高性能。每个特性都是这三个目标的具体体现。本课所学内容是构建真实Rust应用的基石,务必在实践中反复练习巩固。

记住:Rust的学习曲线虽陡,但一旦掌握,你将获得前所未有的编程信心——编译器就是你的最佳队友。

🔧 Axum速查表

// Axum核心类型:
// Router<S>      - 路由器
// MethodRouter    - 方法路由(GET/POST/PUT/DELETE)
// Handler         - 处理器trait
// FromRequest     - 提取器trait
// IntoResponse    - 响应trait
// State<S>        - 状态提取器
// Extension<T>    - 扩展提取器

// 常用提取器:
// axum::extract::Path<T>    - 路径参数
// axum::extract::Query<T>   - 查询参数
// axum::extract::Json<T>    - JSON请求体
// axum::extract::Form<T>    - 表单数据
// axum::extract::State<S>   - 共享状态
// axum::extract::Extension   - 请求扩展
// axum::extract::OriginalUri - 原始URI
// axum::extract::ConnectInfo - 连接信息

// 常用响应:
// axum::Json<T>              - JSON响应
// axum::response::Html        - HTML响应
// axum::response::Redirect    - 重定向
// impl IntoResponse           - 自定义响应
// StatusCode                  - 状态码
// axum::response::AppendHeaders - 追加头

fn main() {
    println!("Axum路由示例:");
    println!("  .route("/", get(handler))");
    println!("  .route("/users", post(create_user))");
    println!("  .route("/users/:id", get(get_user))");
    println!("  .route("/users/:id", put(update_user))");
    println!("  .route("/users/:id", delete(delete_user))");
    
    println!("
中间件:");
    println!("  tower-http::trace::TraceLayer");
    println!("  tower-http::cors::CorsLayer");
    println!("  tower-http::limit::RateLimitLayer");
    println!("  tower-http::timeout::TimeoutLayer");
    println!("  tower-http::compression::CompressionLayer");
    
    println!("
状态管理:");
    println!("  Arc<Mutex<State>>     - 简单共享");
    println!("  Arc<RwLock<State>>    - 读写锁");
    println!("  Arc<DbPool>           - 数据库连接池");
}