npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill axumAxum 是一个基于 Hyper 和 Tower 构建的 Rust Web 框架。它可用于类型安全的请求处理,具备可组合的中间件、结构化的错误处理以及出色的可测试性。
✅ 正确:类型化处理器 + JSON 响应
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();
}
❌ 错误:阻塞异步运行时
async fn handler() {
std::thread::sleep(std::time::Duration::from_secs(1)); // 阻塞执行器
}
处理器是异步函数,返回实现了 IntoResponse 的对象。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
✅ 正确:路由嵌套
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 { "{}" }
在边界处进行解析和验证时,优先使用提取器:
Path<T>:类型化的路径参数Query<T>:查询字符串Json<T>:JSON 请求体State<T>:共享的应用状态✅ 正确:类型化路径 + JSON
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() })
}
使用 State<Arc<AppState>>,并尽可能保持状态不可变。
✅ 正确:通过 Arc 使用 AppState
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)
}
IntoResponse)集中将错误映射到 HTTP 状态码和 JSON。
✅ 正确:AppError 转换为响应
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()
}
}
使用 tower-http 获取生产级的层:追踪、超时、请求 ID、CORS。
✅ 正确:追踪 + 超时 + CORS
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)
}
在收到 SIGINT/SIGTERM 信号时终止,并让正在处理的请求完成。
✅ 正确:with_graceful_shutdown
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();
}
使用 tower::ServiceExt 在不使用套接字的情况下测试路由器。
✅ 正确:请求/响应测试
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);
}
std::thread::sleep、阻塞 I/O)。unwrap();应返回结构化错误。每周安装量
122
代码仓库
GitHub 星标数
22
首次出现
2026年1月23日
安全审计
安装于
gemini-cli102
opencode102
codex98
github-copilot95
claude-code90
cursor85
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.
✅ Correct: typed handler + JSON response
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
async fn handler() {
std::thread::sleep(std::time::Duration::from_secs(1)); // blocks executor
}
Handlers are async functions that return something implementing IntoResponse.
✅ Correct: route nesting
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 { "{}" }
Prefer extractors for parsing and validation at the boundary:
Path<T>: typed path paramsQuery<T>: query stringsJson<T>: JSON bodiesState<T>: shared application state✅ Correct: typed path + JSON
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() })
}
Use State<Arc<AppState>> and keep state immutable where possible.
✅ Correct: AppState via Arc
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)
}
IntoResponse)Centralize error mapping to HTTP status codes and JSON.
✅ Correct: AppError converts into response
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()
}
}
Use tower-http for production-grade layers: tracing, timeouts, request IDs, CORS.
✅ Correct: trace + timeout + CORS
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)
}
Terminate on SIGINT/SIGTERM and let in-flight requests drain.
✅ Correct: with_graceful_shutdown
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();
}
Test routers without sockets using tower::ServiceExt.
✅ Correct: request/response test
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);
}
std::thread::sleep, blocking I/O inside handlers).unwrap() in request paths; return structured errors instead.Weekly Installs
122
Repository
GitHub Stars
22
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli102
opencode102
codex98
github-copilot95
claude-code90
cursor85
lark-cli 共享规则:飞书资源操作指南与权限配置详解
27,500 周安装
YouTube视频智能剪辑工具 - 基于AI自动分析字幕生成精细章节,支持多格式导出和字幕烧录
2,300 周安装
RICE评分法故事优先级排序器 - 基于市场调研的产品功能优先级评估工具
154 周安装
SQL Pro 高级查询与性能优化指南 - CTE、窗口函数、执行计划分析
2,300 周安装
WebSocket工程师指南:构建可扩展实时通信系统的完整工作流程与架构设计
2,200 周安装
macOS设计指南:SwiftUI与AppKit开发规范、菜单栏与键盘快捷键最佳实践
2,200 周安装
Three.js 几何体完整指南:内置形状、高级模型与路径创建教程
2,200 周安装