axum

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Axum (Rust) - Production Web APIs

Axum(Rust)- 生产环境Web API

Overview

概述

Axum is a Rust web framework built on Hyper and Tower. Use it for type-safe request handling with composable middleware, structured errors, and excellent testability.
Axum是基于Hyper和Tower的Rust Web框架。使用它可实现类型安全的请求处理,支持可组合的中间件、结构化错误和出色的可测试性。

Quick Start

快速开始

Minimal server

最小化服务器

Correct: typed handler + JSON response
rust
use axum::{routing::get, Json, Router};
use serde::Serialize;
use std::net::SocketAddr;

#[derive(Serialize)]
struct Health {
    status: &'static str,
}

async fn health() -> Json<Health> {
    Json(Health { status: "ok" })
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/health", get(health));

    let addr: SocketAddr = "0.0.0.0:3000".parse().unwrap();
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}
Wrong: block the async runtime
rust
async fn handler() {
    std::thread::sleep(std::time::Duration::from_secs(1)); // blocks executor
}
正确示例:类型化处理器 + JSON响应
rust
use axum::{routing::get, Json, Router};
use serde::Serialize;
use std::net::SocketAddr;

#[derive(Serialize)]
struct Health {
    status: &'static str,
}

async fn health() -> Json<Health> {
    Json(Health { status: "ok" })
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/health", get(health));

    let addr: SocketAddr = "0.0.0.0:3000".parse().unwrap();
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}
错误示例:阻塞异步运行时
rust
async fn handler() {
    std::thread::sleep(std::time::Duration::from_secs(1)); // blocks executor
}

Core Concepts

核心概念

Router + handlers

路由 + 处理器

Handlers are async functions that return something implementing
IntoResponse
.
Correct: route nesting
rust
use axum::{routing::get, Router};

fn router() -> Router {
    let api = Router::new()
        .route("/users", get(list_users))
        .route("/users/:id", get(get_user));

    Router::new().nest("/api/v1", api)
}

async fn list_users() -> &'static str { "[]" }
async fn get_user() -> &'static str { "{}" }
处理器是返回实现了
IntoResponse
类型的异步函数。
正确示例:路由嵌套
rust
use axum::{routing::get, Router};

fn router() -> Router {
    let api = Router::new()
        .route("/users", get(list_users))
        .route("/users/:id", get(get_user));

    Router::new().nest("/api/v1", api)
}

async fn list_users() -> &'static str { "[]" }
async fn get_user() -> &'static str { "{}" }

Extractors

提取器

Prefer extractors for parsing and validation at the boundary:
  • Path<T>
    : typed path params
  • Query<T>
    : query strings
  • Json<T>
    : JSON bodies
  • State<T>
    : shared application state
Correct: typed path + JSON
rust
use axum::{extract::Path, Json};
use serde::{Deserialize, Serialize};

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

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

async fn create_user(Json(body): Json<CreateUser>) -> Json<User> {
    Json(User { id: "1".into(), email: body.email })
}

async fn get_user(Path(id): Path<String>) -> Json<User> {
    Json(User { id, email: "a@example.com".into() })
}
优先在边界处使用提取器进行解析和验证:
  • Path<T>
    :类型化路径参数
  • Query<T>
    :查询字符串
  • Json<T>
    :JSON请求体
  • State<T>
    :共享应用状态
正确示例:类型化路径 + JSON
rust
use axum::{extract::Path, Json};
use serde::{Deserialize, Serialize};

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

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

async fn create_user(Json(body): Json<CreateUser>) -> Json<User> {
    Json(User { id: "1".into(), email: body.email })
}

async fn get_user(Path(id): Path<String>) -> Json<User> {
    Json(User { id, email: "a@example.com".into() })
}

Production Patterns

生产环境模式

1) Shared state (DB pool, config, clients)

1) 共享状态(数据库连接池、配置、客户端)

Use
State<Arc<AppState>>
and keep state immutable where possible.
Correct: AppState via Arc
rust
use axum::{extract::State, routing::get, Router};
use std::sync::Arc;

#[derive(Clone)]
struct AppState {
    build_sha: &'static str,
}

async fn version(State(state): State<Arc<AppState>>) -> String {
    state.build_sha.to_string()
}

fn app(state: Arc<AppState>) -> Router {
    Router::new().route("/version", get(version)).with_state(state)
}
使用
State<Arc<AppState>>
,尽可能保持状态不可变。
正确示例:通过Arc实现AppState
rust
use axum::{extract::State, routing::get, Router};
use std::sync::Arc;

#[derive(Clone)]
struct AppState {
    build_sha: &'static str,
}

async fn version(State(state): State<Arc<AppState>>) -> String {
    state.build_sha.to_string()
}

fn app(state: Arc<AppState>) -> Router {
    Router::new().route("/version", get(version)).with_state(state)
}

2) Structured error handling (
IntoResponse
)

2) 结构化错误处理(
IntoResponse

Centralize error mapping to HTTP status codes and JSON.
Correct: AppError converts into response
rust
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde::Serialize;

#[derive(Debug)]
enum AppError {
    NotFound,
    BadRequest(&'static str),
    Internal,
}

#[derive(Serialize)]
struct ErrorBody {
    error: &'static str,
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        let (status, msg) = match self {
            AppError::NotFound => (StatusCode::NOT_FOUND, "not_found"),
            AppError::BadRequest(_) => (StatusCode::BAD_REQUEST, "bad_request"),
            AppError::Internal => (StatusCode::INTERNAL_SERVER_ERROR, "internal"),
        };

        (status, Json(ErrorBody { error: msg })).into_response()
    }
}
集中将错误映射为HTTP状态码和JSON格式。
正确示例:AppError转换为响应
rust
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde::Serialize;

#[derive(Debug)]
enum AppError {
    NotFound,
    BadRequest(&'static str),
    Internal,
}

#[derive(Serialize)]
struct ErrorBody {
    error: &'static str,
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        let (status, msg) = match self {
            AppError::NotFound => (StatusCode::NOT_FOUND, "not_found"),
            AppError::BadRequest(_) => (StatusCode::BAD_REQUEST, "bad_request"),
            AppError::Internal => (StatusCode::INTERNAL_SERVER_ERROR, "internal"),
        };

        (status, Json(ErrorBody { error: msg })).into_response()
    }
}

3) Middleware (Tower layers)

3) 中间件(Tower层)

Use
tower-http
for production-grade layers: tracing, timeouts, request IDs, CORS.
Correct: trace + timeout + CORS
rust
use axum::{routing::get, Router};
use std::time::Duration;
use tower::ServiceBuilder;
use tower_http::{
    cors::{Any, CorsLayer},
    timeout::TimeoutLayer,
    trace::TraceLayer,
};

fn app() -> Router {
    let layers = ServiceBuilder::new()
        .layer(TraceLayer::new_for_http())
        .layer(TimeoutLayer::new(Duration::from_secs(10)))
        .layer(CorsLayer::new().allow_origin(Any));

    Router::new()
        .route("/health", get(|| async { "ok" }))
        .layer(layers)
}
使用
tower-http
实现生产级别的中间件层:追踪、超时、请求ID、CORS。
正确示例:追踪 + 超时 + CORS
rust
use axum::{routing::get, Router};
use std::time::Duration;
use tower::ServiceBuilder;
use tower_http::{
    cors::{Any, CorsLayer},
    timeout::TimeoutLayer,
    trace::TraceLayer,
};

fn app() -> Router {
    let layers = ServiceBuilder::new()
        .layer(TraceLayer::new_for_http())
        .layer(TimeoutLayer::new(Duration::from_secs(10)))
        .layer(CorsLayer::new().allow_origin(Any));

    Router::new()
        .route("/health", get(|| async { "ok" }))
        .layer(layers)
}

4) Graceful shutdown

4) 优雅停机

Terminate on SIGINT/SIGTERM and let in-flight requests drain.
Correct: with_graceful_shutdown
rust
async fn shutdown_signal() {
    let ctrl_c = async {
        tokio::signal::ctrl_c().await.ok();
    };

    #[cfg(unix)]
    let terminate = async {
        tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
            .ok()
            .and_then(|mut s| s.recv().await);
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {}
        _ = terminate => {}
    }
}

#[tokio::main]
async fn main() {
    let app = app();
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();

    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await
        .unwrap();
}
在收到SIGINT/SIGTERM信号时终止服务,并等待正在处理的请求完成。
正确示例:with_graceful_shutdown
rust
async fn shutdown_signal() {
    let ctrl_c = async {
        tokio::signal::ctrl_c().await.ok();
    };

    #[cfg(unix)]
    let terminate = async {
        tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
            .ok()
            .and_then(|mut s| s.recv().await);
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {}
        _ = terminate => {}
    }
}

#[tokio::main]
async fn main() {
    let app = app();
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();

    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await
        .unwrap();
}

Testing

测试

Test routers without sockets using
tower::ServiceExt
.
Correct: request/response test
rust
use axum::{body::Body, http::Request, Router};
use tower::ServiceExt;

#[tokio::test]
async fn health_returns_ok() {
    let app: Router = super::app();

    let res = app
        .oneshot(Request::builder().uri("/health").body(Body::empty()).unwrap())
        .await
        .unwrap();

    assert_eq!(res.status(), 200);
}
使用
tower::ServiceExt
无需套接字即可测试路由。
正确示例:请求/响应测试
rust
use axum::{body::Body, http::Request, Router};
use tower::ServiceExt;

#[tokio::test]
async fn health_returns_ok() {
    let app: Router = super::app();

    let res = app
        .oneshot(Request::builder().uri("/health").body(Body::empty()).unwrap())
        .await
        .unwrap();

    assert_eq!(res.status(), 200);
}

Decision Trees

决策树

Axum vs other Rust frameworks

Axum与其他Rust框架对比

  • Prefer Axum for Tower middleware composition and typed extractors.
  • Prefer Actix Web for a mature ecosystem and actor-style runtime model.
  • Prefer Warp for functional filters and minimalism.
  • 若需要Tower中间件组合和类型化提取器,优先选择Axum
  • 若需要成熟的生态系统和 actor 风格的运行时模型,优先选择Actix Web
  • 若需要函数式过滤器和极简设计,优先选择Warp

Anti-Patterns

反模式

  • Block the async runtime (
    std::thread::sleep
    , blocking I/O inside handlers).
  • Use
    unwrap()
    in request paths; return structured errors instead.
  • Run without timeouts; add request timeouts and upstream deadlines.
  • 阻塞异步运行时(在处理器中使用
    std::thread::sleep
    、阻塞式I/O)。
  • 在请求处理路径中使用
    unwrap()
    ;应返回结构化错误。
  • 不设置超时;需添加请求超时和上游截止时间。

Resources

参考资源