其他
用 Axum 构建生产级 Rust 异步 Web API…
在 Rust + Tokio/Tower 技术栈中构建 REST API、WebSocket/SSE 或微服务时,使用 Axum 的类型安全提取器、Router、Layer 和 IntoResponse 统一组织请求处理。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
用 Axum 构建生产级 Rust 异步通用
解决思路
- 以 Tower 的 Service trait 为组合基础,把路由、提取器、中间件都视为可组合服务。
- 使用类型安全提取器在编译期检查请求数据,避免运行时解析错误。
- 保持 API 显式但减少样板,用 async handler 直接返回 IntoResponse。
- 使用 `Router::new()` 创建应用,并用 `.route('/', get(handler))` 注册路径与 HTTP 方法。
- 同一路径链式注册多方法,例如 `get(list_users).post(create_user)` 或 `get(...).post(...).put(...).delete(...).patch(...)`。
- 使用 `.nest('/api', api_routes())` 嵌套路由器实现模块化;父级匹配前缀会从 URI 剥离,子路由器只看到相对路径。
- 使用 `.fallback(fallback)` 处理未匹配路由;子路由器未定义 fallback 时会继承父级 fallback。
- 路由匹配按定义顺序进行;可通过 `Router` 的泛型状态参数管理不同状态。
- Handler 必须是 async 函数,可包含最多 16 个参数且全部是提取器;body 提取器必须放在最后,且一个 handler 不能有多个 body 提取器。
- 使用 `Path<u32>` 提取单个路径参数;多个参数用 `Path<(u32, u32)>` 或实现 `serde::Deserialize` 的结构体。
- 使用 `Query<T>` 加 `serde::Deserialize` 提取查询字符串,字段用 `Option` 表示可选,用 `unwrap_or` 提供默认值。
- 使用 `Json<T>` 加 `serde::Deserialize` 解析 JSON 请求体;返回 `Json<T>` 自动序列化为 JSON 响应。
- 对 JSON 提取失败,使用 `Result<Json<T>, JsonRejection>` 匹配 `MissingJsonContentType`、`JsonDataError`、`JsonSyntaxError` 并返回 `BAD_REQUEST` 与具体错误。
- 使用 `State<T>` 提取共享状态;状态类型必须实现 `Clone`,昂贵共享资源用 `Arc` 包装。
- 使用 `Extension<T>` 提取请求扩展;中间件先通过 `req.extensions_mut().insert(user)` 插入数据,handler 用 `Extension<CurrentUser>` 读取。
- 使用 `Form<T>` 加 `serde::Deserialize` 解析 form-encoded 请求体。
- 使用 `HeaderMap` 读取请求头,如 `headers.get('user-agent').and_then(|v| v.to_str().ok())`。
- 内置提取器还包括 `Bytes`、`String`、`Method`、`Uri`、`Request`、`Multipart`;按需求选择。
- 实现 `FromRequestParts` 或 `FromRequest` 创建自定义提取器;使用 `async_trait`,设置 `type Rejection`,例如从 `authorization` 头验证用户。
- 任何实现 `IntoResponse` 的类型都可作为 handler 返回值;内置包括 `String`、`&'static str`、`Json<T>`、`Html<String>`、`StatusCode`、`(StatusCode, T)`、`(Parts, T)`、`Response`、`Result<T, E>`。
- 使用 `.layer()` 在 Router、route 或 method 级别应用中间件;`Router::layer` 在路由后运行,`Layer::layer` 包裹 Router 在路由前运行。
- 使用 `ServiceBuilder` 组合多个 Layer,执行顺序为 top-to-bottom;直接链式 `.layer()` 执行顺序为 bottom-to-top。
- 使用 `tower_http::trace::TraceLayer::new_for_http()` 记录 HTTP 请求追踪。
- 使用 `CompressionLayer` 压缩响应体。
- 使用 `CorsLayer` 配置 CORS,例如 `allow_origin(Any)`、`allow_methods(Any)`、`allow_headers(Any)`。
- 使用 `tower::timeout::TimeoutLayer::new(Duration::from_secs(30))` 设置请求超时。
- 使用 `axum::error_handling::HandleErrorLayer` 将中间件错误转换为 HTTP 响应,例如对 `tower::timeout::error::Elapsed` 返回 `REQUEST_TIMEOUT`。
- 使用 `axum::middleware::from_fn` 编写自定义中间件;签名如 `async fn(req: Request, next: Next) -> Result<Response, StatusCode>`,并调用 `next.run(req).await`。
- 在 `from_fn` 中间件中通过 `req.extensions_mut().insert(user)` 把认证用户传给后续 handler。
- 实现 Tower 的 `Service` 和 `Layer` trait 创建自定义 Tower 中间件;在 `call` 中包装 `inner.call(request)`,用 `BoxFuture` 返回异步结果。
- 创建自定义 Layer 时持有 `AppState` 副本,在 `call` 中使用状态;然后用 `.layer(MyLayer { state })` 并 `.with_state(state)` 注册。
- 需要路由前中间件(如 URI 重写)时,用 `tower::util::MapRequestLayer` 包裹整个 Router,再通过 `axum::serve` 启动。
- 使用 `.with_state(state)` 把状态注入 Router;简单状态直接 Clone 结构体,共享昂贵资源用 `Arc<DatabasePool>`、`Arc<RwLock<HashMap<...>>>`。
- 对不同路由段使用不同状态类型:分别定义 `Router<ApiState>` 和 `Router<AppState>`,各自 `with_state` 后再 `nest` 合并。
- 编写泛型状态路由函数 `fn routes<S>() -> Router<S> where S: Clone + Send + Sync + 'static`,以便与任意状态合并。
- 使用状态转换链:`Router<StateA>` 调 `with_state(StateA)` 后变为 `Router<StateB>`,再添加需要 `StateB` 的路由,最后 `with_state(StateB)` 得到 `Router<()>`。
- 处理错误时使用 `Result<T, E>`,其中 `E: IntoResponse`;简单场景可直接返回 `Result<String, StatusCode>`。
- 定义自定义错误枚举并实现 `IntoResponse`;按变体映射 `StatusCode`,记录 `tracing::error!`,返回 `Json` 错误体。
- 在 handler 中用 `map_err` 把底层错误转成 `AppError`,如 `db.get_user(id).await.map_err(AppError::Database)?`,再用 `ok_or(AppError::NotFound)` 转 404。
- 对提取器 rejection 使用 `Result<Json<T>, JsonRejection>` 并 match 各 rejection 类型,转为自定义 `AppError::ValidationError`。
- 创建 `ValidatedJson<T>` 自定义提取器:先 `Json::<T>::from_request`,再调用 `T: Validate` 的 `validate()`,失败返回 `BAD_REQUEST`。
- 对 fallible middleware 使用 `HandleErrorLayer`;对 fallible service 使用 `HandleError::new(fallible_service, handle_error)` 并 `route_service`。
- 构建 JSON 响应:返回 `Json(ApiResponse { ... })`;构建 HTML 响应:返回 `Html('<h1>Hello</h1>')` 或 `Html(format!(...))`。
- 返回纯状态码用 `StatusCode`;返回状态码加 body 用 `(StatusCode, T)`;返回带自定义头的响应用 `(HeaderMap, T)`。
- 使用 `Response::builder()` 完全控制响应:设置 `status`、`header(header::CONTENT_TYPE, 'application/json')`、`body(Body::from(...))`。
- 使用 `Sse` 和 `futures::stream` 实现 Server-Sent Events;返回 `Sse<impl Stream<Item = Result<Event, Infallible>>>`。
- 实现文件下载:`tokio::fs::File::open` 打开文件,`Body::from_stream(ReaderStream::new(file))` 作为 body,设置 `CONTENT_TYPE` 和 `CONTENT_DISPOSITION: attachment; filename=file.pdf`。
- 集成数据库:用 `sqlx::PgPool` 和 `PgPoolOptions` 创建连接池,存入 `AppState`;handler 中 `query_as!` 查询,`fetch_optional`,`map_err` 转 `AppError`。
- 配置管理:用 `config` crate 的 `Config::builder().add_source(Environment::default()).build()?.try_deserialize()` 读取 `Settings`;包含 `database_url`、`redis_url`、`jwt_secret`、`port`。
- 结构化日志:用 `tracing_subscriber::registry().with(EnvFilter::try_from_default_env()).with(fmt::layer()).init()`;用 `info/error/debug` 和 `#[instrument]` 记录 handler。
- 优雅关闭:定义 `shutdown_signal` 监听 `ctrl_c` 和 SIGTERM,在 `axum::serve(listener, app).with_graceful_shutdown(shutdown_signal())` 中等待关闭。
- 健康检查:添加 `/health` 和 `/ready` 路由;`health_check` 查询数据库 `SELECT 1` 和 Redis `ping`,返回 `Json<HealthResponse>` 表示 healthy/degraded。
- 速率限制:使用 `tower::limit::RateLimitLayer::new(100, Duration::from_secs(60))` 限制每分钟请求数。
- 请求验证:在结构体上使用 `validator::Validate` 的 `#[validate(length(min = 3, max = 50))]`、`#[validate(email)]` 等;通过 `ValidatedJson` 提取器自动验证。
- 单元测试 handler:用 `Router::new().route(...)` 构建 app,用 `tower::ServiceExt::oneshot` 发送 `Request::builder().uri(...).body(Body::empty())`,断言 `response.status()`。
- 集成测试:构造测试 `AppState` 和 `create_app`,发送 POST JSON 请求,断言 `StatusCode::CREATED`。
- Handler 组织最佳实践:保持 handler 精简,业务逻辑移到 service 层;用提取器做类型化提取;返回 Result 和自定义错误;用 tracing 增加可观测性。
- 状态管理最佳实践:昂贵类型用 `Arc`;状态尽量最小;必须实现 `Clone`;需要修改时用 `RwLock`/`Mutex` 等内部可变性。
- 错误处理最佳实践:定义自定义错误并实现 `IntoResponse`;提供上下文和有用消息;用 tracing 记录;返回符合 HTTP 语义的状态码。
- 中间件最佳实践:优先 `ServiceBuilder`;在正确层级(Router/route/method)应用;用 `HandleErrorLayer` 处理可失败中间件;保持单一职责。
- 性能最佳实践:数据库和外部服务用连接池;启用 `CompressionLayer`;实现缓存;用 `LoadShedLayer`/`RateLimitLayer` 提供背压;优化 JSON 序列化。
- 安全最佳实践:验证输入;用中间件实现认证;生产使用 HTTPS;设置 CORS/CSP 等安全头;速率限制防止滥用。
- Repository 模式:定义 `trait UserRepository` 含 `get/create/update/delete`;实现 `PostgresUserRepository`;`AppState` 保存 `Arc<dyn UserRepository + Send + Sync>`;handler 通过 `state.user_repo` 调用。
- Service 层模式:定义 `UserService` 持有 repo 和 `EmailService`;在 `create_user` 中调用 `repo.create` 后发送欢迎邮件;`AppState` 保存 `Arc<UserService>`。
- 版本化 API:定义 `v1_routes()` 和 `v2_routes()` 返回 `Router<AppState>`,用 `.nest('/api/v1', v1_routes())` 和 `.nest('/api/v2', v2_routes())` 注册不同版本。
- 使用 `.route_layer(middleware::from_fn(auth_middleware))` 在特定路由应用认证中间件,而不影响其他路由。
- 对同一路由不同方法应用不同中间件:在 `get(handler).route_layer(read_only_auth).post(create_handler).route_layer(write_auth)` 链式调用。
- 使用 `tokio::net::TcpListener::bind('0.0.0.0:3000').await` 创建监听器,用 `axum::serve(listener, app).await` 启动服务。
- 当 app 被 Layer 包裹后,用 `axum::serve(listener, app.into_make_service()).await` 启动。
- 使用通配路由 `/{*key}` 捕获剩余路径,`Path<String>` 获得剩余段;注意嵌套路由器会剥离前缀,但通配路由保留完整 URI。
- 使用 `.route('/posts/:id/comments', get(get_comments).post(add_comment))` 注册嵌套资源路径。
- 使用 `serde::{Deserialize, Serialize}` 为请求和响应结构体派生序列化/反序列化。
- 使用 `axum::http::{StatusCode, Method, Uri, HeaderMap, header}` 和 `axum::body::Body` 构建响应与测试。
- 使用 `axum::extract::rejection::JsonRejection` 精确处理 JSON 提取错误。
- 使用 `std::sync::Arc` 和 `tokio::sync::RwLock` 共享和修改状态。
- 使用 `tokio::signal` 监听系统信号,实现优雅关闭。
- 使用 `tokio_util::io::ReaderStream` 将文件流转换为可流式响应体。
- 使用 `futures::stream::{self, Stream}` 构建 SSE 事件流。
- 使用 `validator::{Validate, ValidationError}` 做结构体字段验证。
- 使用 `config::{Config, ConfigError, Environment}` 从环境变量加载配置。
- 使用 `tracing::{info, error, debug, instrument}` 和 `tracing_subscriber` 输出结构化日志。
- 使用 `tower::limit::RateLimitLayer` 限制请求速率。
- 使用 `tower::timeout::TimeoutLayer` 为请求设置超时。
- 使用 `tower_http::cors::{CorsLayer, Any}` 配置跨域。
- 使用 `tower_http::compression::CompressionLayer` 压缩响应。
- 使用 `tower_http::trace::TraceLayer` 记录请求。
- 使用 `sqlx::{PgPool, postgres::PgPoolOptions}` 连接 PostgreSQL。
- 使用 `tokio::fs::File` 和 `axum::body::Body` 实现文件下载。
- 使用 `axum::response::sse::{Event, Sse}` 实现 SSE。
- 使用 `axum::response::Html` 返回 HTML。
- 使用 `axum::Json` 返回 JSON。
- 使用 `axum::extract::Path`、`Query`、`Json`、`State`、`Extension`、`Form`、`HeaderMap` 等内置提取器。
适用边界 · 注意事项
- 如果项目不是 Rust 或不需要异步 HTTP 服务,不要使用 Axum。
- 如果团队不熟悉 Tokio/Tower/异步 Rust,先用更简单框架或投入学习成本。
- 如果需要自带 ORM、模板、管理后台的全栈框架,Axum 较底层,不适合直接套用。