axum
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAxum (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:
- : typed path params
Path<T> - : query strings
Query<T> - : JSON bodies
Json<T> - : shared application state
State<T>
✅ 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请求体
Json<T> - :共享应用状态
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 and keep state immutable where possible.
State<Arc<AppState>>✅ 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
)
IntoResponse2) 结构化错误处理(IntoResponse
)
IntoResponseCentralize 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 for production-grade layers: tracing, timeouts, request IDs, CORS.
tower-http✅ 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)
}使用实现生产级别的中间件层:追踪、超时、请求ID、CORS。
tower-http✅ 正确示例:追踪 + 超时 + 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 (, blocking I/O inside handlers).
std::thread::sleep - Use in request paths; return structured errors instead.
unwrap() - Run without timeouts; add request timeouts and upstream deadlines.
- 阻塞异步运行时(在处理器中使用、阻塞式I/O)。
std::thread::sleep - 在请求处理路径中使用;应返回结构化错误。
unwrap() - 不设置超时;需添加请求超时和上游截止时间。
Resources
参考资源
- Axum docs: https://docs.rs/axum
- Tower HTTP layers: https://docs.rs/tower-http
- Tracing: https://docs.rs/tracing
- Axum文档:https://docs.rs/axum
- Tower HTTP中间件层:https://docs.rs/tower-http
- Tracing:https://docs.rs/tracing