Axum 入门

定位

Axum 是 Tokio 官方维护的 Web 框架,建在 hyper 和 tower 之上。它是当前 Rust 后端的默认答案,核心原因是能直接用整个 tower 生态的中间件。

设计极简:一个 handler 就是普通 async fn,参数通过实现 FromRequest 的类型提取。

安装

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

注意 axum 还在 0.x(0.8.9),跨小版本升级有破坏性变更,建议锁版本。

最小示例

use axum::{
    Router,
    routing::{get, post},
    extract::Path,
    Json,
};
use serde::{Deserialize, Serialize};

#[derive(Serialize)]
struct User { id: u64, name: String }

#[derive(Deserialize)]
struct CreateUser { name: String }

async fn hello() -> &'static str {
    "Hello"
}

async fn get_user(Path(id): Path<u64>) -> Json<User> {
    Json(User { id, name: "demo".into() })
}

async fn create_user(Json(payload): Json<CreateUser>) -> Json<User> {
    Json(User { id: 1, name: payload.name })
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(hello))
        .route("/users/{id}", get(get_user))
        .route("/users", post(create_user));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

注意路径参数语法:axum 0.8 起用 {id},0.7 及以前是 :id。抄旧教程会失败。

核心概念

1. Extract(提取器)

handler 的参数可以是任何实现了 FromRequestParts 或 FromRequest 的类型:

类型提取什么
Path<T>路径参数
Query<T>查询字符串
Json<T>JSON body
State<S>应用共享状态
HeaderMap请求头
TypedHeader<T>类型化的请求头

提取器最多只能有一个消费 body 的(比如 Json<T>),而且必须放在参数列表最后。

2. State:共享状态

use axum::extract::State;

#[derive(Clone)]
struct AppState { db: Pool }

async fn handler(State(state): State<AppState>) -> String {
    // 用 state.db
    String::new()
}

let app = Router::new()
    .route("/", get(handler))
    .with_state(AppState { db: pool });

AppState 必须实现 Clone——Axum 每次请求都克隆一份。所以里面放大对象要用 Arc 包。

3. 中间件:直接用 tower

这是 Axum 最大的优势。tower 生态的中间件不需要适配:

use tower_http::{
    trace::TraceLayer,
    cors::CorsLayer,
    compression::CompressionLayer,
    timeout::TimeoutLayer,
};
use std::time::Duration;

let app = Router::new()
    .route("/", get(hello))
    .layer(TraceLayer::new_for_http())
    .layer(CorsLayer::permissive())
    .layer(CompressionLayer::new())
    .layer(TimeoutLayer::new(Duration::from_secs(10)));

只要库是为 tower 写的,就能直接用。 这是它比 Actix 更受欢迎的核心原因。

常见坑

① 版本号敏感

0.6 → 0.7 改了提取器 trait 名称,0.7 → 0.8 改了路径参数语法。文档和教程混着看会浪费大量时间。以官方 CHANGELOG 为准。

② 编译慢

第一次全量编译 axum + tokio + serde 要几分钟。日常开发用 cargo check,比 cargo build 快得多。

③ 错误处理要自己设计

Axum 没有内置的错误响应格式。惯例是实现 IntoResponse:

enum AppError { NotFound, Internal(anyhow::Error) }

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, msg) = match self {
            AppError::NotFound      => (StatusCode::NOT_FOUND, "not found"),
            AppError::Internal(e)   => (StatusCode::INTERNAL_SERVER_ERROR, "internal"),
        };
        (status, msg).into_response()
    }
}

handler 返回 Result<T, AppError> 就能统一处理。

④ State 里的锁用错

Arc<Mutex<T>> 在 await 期间持有锁会导致死锁(因为 MutexGuard 不是 Send)。异步场景要用 tokio::sync::Mutex。

什么时候选它

  • 绝大多数 Rust 后端场景(生态最主流)
  • 需要 tower 中间件(限流、追踪、压缩、超时)
  • 和 sqlx、tonic、tracing 配合使用

不选它的场景:需要极致的吞吐(Actix 略强)、只想快速出原型(Rocket 更友好)、团队 Rust 经验不足。

一个现实提醒

Rust Web 开发的成本主要在编译和类型系统,不在框架。

改一行代码等 30 秒编译,这是日常。如果你要反复调 UI 逻辑或业务规则,Rust 的开发效率会明显低于 Go/Node。

真正值得用 Rust 的场景:CPU 密集(图像处理、加解密、协议解析)、内存受限、需要极致低延迟、已经有 Rust 代码库要集成。

否则,Go 能解决的问题用 Rust 做,通常是「付出三倍开发时间换来 10% 的性能提升」。

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇