Axum Code Review
Review Workflow
- 1. Check Cargo.toml — Note axum version (0.6 vs 0.7+ have different patterns), Rust edition (2021 vs 2024), tower, tower-http features. Edition 2024 changes RPIT lifetime capture in handler return types and removes the need for
async-trait in custom extractors. - Check routing — Route organization, method routing, nested routers
- Check extractors — Order matters (body extractors must be last), correct types
- Check state — Shared state via
State<T>, not global mutable state - Check error handling —
IntoResponse implementations, error types
Output Format
Report findings as:
CODEBLOCK0
Quick Reference
| Issue Type | Reference |
|---|
| Route definitions, nesting, method routing | references/routing.md |
| State, Path, Query, Json, body extractors |
references/extractors.md |
| Tower middleware, layers, error handling |
references/middleware.md |
Review Checklist
Routing
- - [ ] Routes organized by domain (nested routers for
/api/users, /api/orders) - [ ] Fallback handlers defined for 404s
- [ ] Method routing explicit (
.get(), .post(), not .route() with manual method matching) - [ ] No route conflicts (overlapping paths with different extractors)
Extractors
- - [ ] Body-consuming extractors (
Json, Form, Bytes) are the LAST parameter - [ ]
State<T> requires T: Clone — typically Arc<AppState> or direct Clone derive - [ ]
Path<T> parameter types match the route definition - [ ]
Query<T> fields are Option for optional query params with INLINECODE18 - [ ] Custom extractors implement
FromRequestParts (not body) or FromRequest (body) - [ ] Edition 2024: Custom extractors use native
async fn in trait impls (no #[async_trait] needed for FromRequest/FromRequestParts)
State Management
- - [ ] Application state shared via
State<T>, not global mutable statics - [ ] Database pool in state (not created per-request)
- [ ] State contains only shared resources (pool, config, channels), not request-specific data
- [ ]
Clone derived or manually implemented on state type - [ ] Edition 2024: Shared static state uses
LazyLock from std (not once_cell::sync::Lazy or lazy_static!)
Error Handling
- - [ ] Handler errors implement
IntoResponse for proper HTTP error codes - [ ] Internal errors don't leak to clients (no raw error messages in 500 responses)
- [ ] Error responses use consistent format (JSON error body with code/message)
- [ ]
Result<impl IntoResponse, AppError> pattern used for handlers - [ ] Edition 2024: Handler return types
-> impl IntoResponse capture all in-scope lifetimes by default; use + use<> to opt out of capturing request lifetimes when returning owned data
Middleware
- - [ ] Tower layers applied in correct order (outer runs first on request, last on response)
- [ ]
tower-http used for common concerns (CORS, compression, tracing, timeout) - [ ] Request-scoped data passed via extensions, not global state
- [ ] Middleware errors don't panic — they return error responses
- [ ] Edition 2024: Middleware using
#[async_trait] can migrate to native async fn in trait impls
Severity Calibration
Critical
- - Body extractor not last in handler parameters (silently consumes body, later extractors fail)
- SQL injection via path/query parameters passed directly to queries
- Internal error details leaked to clients (stack traces, database errors)
- Missing authentication middleware on protected routes
Major
- - Global mutable state instead of
State<T> (race conditions) - Missing error type conversion (raw
sqlx::Error returned to client) - Missing request timeout (handlers can hang indefinitely)
- Route conflicts causing unexpected 405s
- Edition 2024:
async-trait still used for FromRequest/FromRequestParts when native async fn works
Minor
- - Manual route method matching instead of
.get(), INLINECODE43 - Missing fallback handler (default 404 is plain text, not JSON)
- Middleware applied per-route when it should be global (or vice versa)
- Missing
tower-http::trace for request logging - Edition 2024:
once_cell::sync::Lazy or lazy_static! used where std::sync::LazyLock works
Informational
- - Suggestions to use
tower-http layers for common concerns - Router organization improvements
- Suggestions to add OpenAPI documentation via
utoipa or INLINECODE50
Valid Patterns (Do NOT Flag)
- -
#[axum::debug_handler] on handlers — Debugging aid that improves compile error messages Extension<T> for middleware-injected data — Valid pattern for request-scoped values- Returning
impl IntoResponse from handlers — More flexible than concrete types Router::new() per module, merged in main — Standard organization patternServiceBuilder for layer composition — Tower pattern, not over-engineeringaxum::serve with TcpListener — Standard axum 0.7+ server setup- Native
async fn in FromRequest/FromRequestParts impls — async-trait crate no longer needed (stable since Rust 1.75) + use<'a> on handler return types — Edition 2024 precise capture syntax for RPITstd::sync::LazyLock for shared static state — Replaces once_cell/lazy_static (stable since Rust 1.80)
Before Submitting Findings
Load and follow beagle-rust:review-verification-protocol before reporting any issue.
Axum 代码审查
审查工作流
- 1. 检查 Cargo.toml — 注意 axum 版本(0.6 与 0.7+ 模式不同)、Rust 版本(2021 与 2024)、tower、tower-http 特性。2024 版本改变了处理程序返回类型中 RPIT 生命周期的捕获方式,并消除了自定义提取器中对 async-trait 的需求。
- 检查路由 — 路由组织、方法路由、嵌套路由器
- 检查提取器 — 顺序至关重要(主体提取器必须放在最后)、正确的类型
- 检查状态 — 通过 State 共享状态,而非全局可变状态
- 检查错误处理 — IntoResponse 实现、错误类型
输出格式
按以下格式报告发现:
text
[文件:行号] 问题标题
严重程度:严重 | 主要 | 次要 | 信息性
问题描述及其重要性。
快速参考
references/extractors.md |
| Tower 中间件、层、错误处理 |
references/middleware.md |
审查清单
路由
- - [ ] 按领域组织路由(针对 /api/users、/api/orders 使用嵌套路由器)
- [ ] 为 404 定义了回退处理程序
- [ ] 方法路由明确(使用 .get()、.post(),而非通过手动方法匹配的 .route())
- [ ] 无路由冲突(不同提取器的重叠路径)
提取器
- - [ ] 消耗主体的提取器(Json、Form、Bytes)是最后一个参数
- [ ] State 要求 T: Clone — 通常使用 Arc 或直接派生 Clone
- [ ] Path 参数类型与路由定义匹配
- [ ] Query 字段对于可选查询参数使用 Option 并添加 #[serde(default)]
- [ ] 自定义提取器实现 FromRequestParts(非主体)或 FromRequest(主体)
- [ ] 2024 版本:自定义提取器在 trait 实现中使用原生 async fn(FromRequest/FromRequestParts 无需 #[async_trait])
状态管理
- - [ ] 通过 State 共享应用状态,而非全局可变静态变量
- [ ] 数据库连接池在状态中(非每次请求创建)
- [ ] 状态仅包含共享资源(连接池、配置、通道),不包含请求特定数据
- [ ] 在状态类型上派生或手动实现 Clone
- [ ] 2024 版本:共享静态状态使用 std 中的 LazyLock(而非 oncecell::sync::Lazy 或 lazystatic!)
错误处理
- - [ ] 处理程序错误实现 IntoResponse 以返回正确的 HTTP 错误码
- [ ] 内部错误不泄露给客户端(500 响应中不包含原始错误消息)
- [ ] 错误响应使用一致格式(包含 code/message 的 JSON 错误体)
- [ ] 处理程序使用 Result 模式
- [ ] 2024 版本:处理程序返回类型 -> impl IntoResponse 默认捕获所有作用域内的生命周期;返回自有数据时使用 + use<> 选择不捕获请求生命周期
中间件
- - [ ] Tower 层按正确顺序应用(外层在请求时先运行,在响应时后运行)
- [ ] 使用 tower-http 处理常见关注点(CORS、压缩、追踪、超时)
- [ ] 请求作用域数据通过扩展传递,而非全局状态
- [ ] 中间件错误不会 panic — 它们返回错误响应
- [ ] 2024 版本:使用 #[async_trait] 的中间件可迁移到 trait 实现中的原生 async fn
严重程度校准
严重
- - 主体提取器未放在处理程序参数最后(静默消耗主体,后续提取器失败)
- 通过路径/查询参数直接传入查询的 SQL 注入
- 内部错误详情泄露给客户端(堆栈跟踪、数据库错误)
- 受保护路由缺少认证中间件
主要
- - 使用全局可变状态而非 State(竞态条件)
- 缺少错误类型转换(原始 sqlx::Error 返回给客户端)
- 缺少请求超时(处理程序可能无限挂起)
- 路由冲突导致意外的 405 错误
- 2024 版本:当原生 async fn 可用时仍对 FromRequest/FromRequestParts 使用 async-trait
次要
- - 手动路由方法匹配而非使用 .get()、.post()
- 缺少回退处理程序(默认 404 为纯文本,非 JSON)
- 中间件按路由应用而应为全局(或反之)
- 缺少 tower-http::trace 用于请求日志记录
- 2024 版本:在 std::sync::LazyLock 可用时仍使用 oncecell::sync::Lazy 或 lazystatic!
信息性
- - 建议使用 tower-http 层处理常见关注点
- 路由器组织改进
- 建议通过 utoipa 或 aide 添加 OpenAPI 文档
有效模式(请勿标记)
- - 处理程序上的 #[axum::debughandler] — 调试辅助工具,改善编译错误消息
- 用于中间件注入数据的 Extension — 请求作用域值的有效模式
- 从处理程序返回 impl IntoResponse — 比具体类型更灵活
- 每个模块的 Router::new(),在 main 中合并 — 标准组织模式
- 用于层组合的 ServiceBuilder — Tower 模式,非过度设计
- 使用 TcpListener 的 axum::serve — 标准 axum 0.7+ 服务器设置
- FromRequest/FromRequestParts 实现中的原生 async fn — 不再需要 async-trait crate(自 Rust 1.75 起稳定)
- 处理程序返回类型上的 + use — 2024 版本中 RPIT 的精确捕获语法
- 用于共享静态状态的 std::sync::LazyLock — 替代 oncecell/lazy_static(自 Rust 1.80 起稳定)
提交发现前
在报告任何问题前,加载并遵循 beagle-rust:review-verification-protocol。