Axum

作者 bobmatnyc718070a7d622MIT77 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

Axum (Rust) web framework patterns for production APIs: routers/extractors, state, middleware, error handling, tracing, graceful shutdown, and testing

AI 產生的概覽

使用 Axum 框架建構正式環境 Rust HTTP API 的參考指南,涵蓋路由、擷取器、中介軟體、錯誤處理與測試。

功能
提供使用 Axum 建構 Rust HTTP 服務的模式說明與程式碼範例,包括具型別的处理函式與擷取器、共用狀態、結構化錯誤回應、Tower 中介軟體層、追蹤、逾時、優雅關閉以及路由測試。也涵蓋帶有選用 HTTP 功能的 crate 結構,以及受監管環境下的關閉維運說明。產出的是指引內容與範例程式碼,而非產生的檔案或指令碼。
適用情境
適用於建構或審查需要具型別請求處理、可組合中介軟體、結構化錯誤、可觀測性與優雅關閉的 Axum Rust HTTP API 或服務。也適用於在 Axum 與其他 Rust Web 框架之間做選擇,或在不開啟通訊端的情況下測試路由。
執行需求
不包含指令碼,僅為說明性內容。依範例操作需要 Rust 工具鏈以及 axum、tokio、tower、tower-http、serde 和 tracing 等 crate,並需要網路存取以下載相依套件。

Axum (Rust) - Production Web APIs

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.

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}

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 { "{}" }

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: "[email protected]".into() })}

Dependencies & Crate Structure

If your crate is published as a library in addition to being built as a binary, isolate the HTTP stack to avoid bloating library consumers.

✅ Correct: optional HTTP feature

toml
[dependencies]axum = { version = "0.7", optional = true }tower-http = { version = "0.5", optional = true }tokio = { version = "1", features = ["full"] }
[features]default = ["http-server"]http-server = ["axum", "tower-http"]
[[bin]]name = "my-service"required-features = ["http-server"]

This way:

  • Library consumers (cargo add my-lib) get just the core logic without the HTTP overhead.
  • Binary builds include the server by default: cargo install my-crate works as expected.
  • Opt-out is explicit: cargo add my-crate --no-default-features for library use.

❌ Wrong: unconditional HTTP dependencies

toml
# Never do this if the crate is also a library:axum = "0.7"tower-http = "0.5"# Library users now pull in the entire web stack

Production Patterns

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

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)}

2) Structured error handling (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()    }}

3) Middleware (Tower layers)

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)}

4) Graceful shutdown

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();}
Ops: Graceful Shutdown in Supervised Environments

Signal handling above is application-level. In production, your supervisor (systemd, launchd, container orchestrator) controls the actual termination window.

systemd (Linux): Set KillSignal=SIGTERM and TimeoutStopSec=120 (or higher) in your .service file.

  • The default 90s timeout can fire mid-fsync on networked storage (EBS/EFS), truncating writes.
  • 120s gives in-flight HTTP requests time to drain plus a safety margin for filesystem syncs.

launchd (macOS): Use launchctl bootout (sends SIGTERM and waits) instead of launchctl kickstart -k (SIGKILL, truncates in-flight I/O).

Client-side reconnection: Have HTTP clients and MCP bridges connecting to the service implement exponential backoff (starting 200ms, capped at 30s) so brief restarts are transparent and don't cascade errors upstream.

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);}

Decision Trees

Axum vs other Rust frameworks

  • 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.

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.

Resources

來源與署名

來源:bobmatnyc/claude-mpm-skills位於toolchains/rust/frameworks/axum提交718070a

授權條款: MIT

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架