中间件、鉴权与错误处理
CubeAPI 的中间件栈分为两层:全局中间件(所有请求)和路由级中间件(sandbox / template / cluster 路由)。这篇文章拆解每一层的实现。
1. 全局中间件栈
所有请求都经过以下 5 层中间件,顺序固定:
rust
// CubeAPI/src/routes.rs
ServiceBuilder::new()
.layer(SetRequestIdLayer::x_request_id(MakeRequestUuid))
.layer(TraceLayer::new_for_http())
.layer(TimeoutLayer::new(Duration::from_secs(30)))
.layer(CompressionLayer::new())
.layer(CorsLayer::permissive())SetRequestId
每个请求分配一个 UUID,写入 X-Request-Id 响应头。下游服务(CubeMaster)可以透传这个 ID,方便全链路追踪。
TraceLayer
基于 tracing crate 的请求追踪,记录:
- 请求方法、路径、状态码
- 响应时间
- 错误信息
TimeoutLayer
30 秒硬超时。如果 handler 没有在 30 秒内返回,中间件直接返回 503。
CompressionLayer
支持 gzip 和 br (Brotli) 压缩,根据客户端 Accept-Encoding 自动选择。
CorsLayer
CorsLayer::permissive() 允许所有来源、所有方法、所有头部。适合开发环境,生产环境建议收紧。
2. 路由级中间件
2.1 sandbox 路由:鉴权 + 限流
2.2 template / cluster 路由:仅鉴权
rust
// routes.rs
// sandbox 路由
fn with_auth_and_rate_limit(routes, state, auth_configured) {
routes
.layer(middleware::from_fn_with_state(state.clone(), rate_limit))
.layer(middleware::from_fn_with_state(state.clone(), unified_auth))
}
// template / cluster 路由
fn with_auth(routes, state, auth_configured) {
routes.layer(middleware::from_fn_with_state(state.clone(), unified_auth))
}3. unified_auth:鉴权回调机制
CubeAPI/src/middleware/auth.rs 实现了统一鉴权逻辑。
3.1 鉴权流程
3.2 关键实现
rust
// CubeAPI/src/middleware/auth.rs
pub async fn unified_auth(
State(state): State<AppState>,
req: Request,
next: Next,
) -> Result<Response, AppError> {
let auth_url = match &state.config.auth_callback_url {
Some(url) => url,
None => return Ok(next.run(req).await), // 未配置则放行
};
let auth_header = req.headers()
.get(header::AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.unwrap_or("");
let client = &state.http_client;
let resp = client.post(auth_url.as_str())
.header(header::AUTHORIZATION, auth_header)
.send()
.await?;
if resp.status().is_success() {
Ok(next.run(req).await)
} else {
Err(AppError::Unauthorized)
}
}3.3 配置方式
bash
# 环境变量
export AUTH_CALLBACK_URL=http://your-auth-service/validate
# CLI
--auth-callback-url http://your-auth-service/validate4. rate_limit:基于 API Key 的限流
CubeAPI/src/middleware/rate_limit.rs 使用 governor crate 实现 per-key 令牌桶限流。
4.1 限流流程
4.2 关键实现
rust
// CubeAPI/src/middleware/rate_limit.rs
pub async fn rate_limit(
State(state): State<AppState>,
req: Request,
next: Next,
) -> Result<Response, AppError> {
let key = extract_api_key(&req); // 从 Authorization header 提取
match state.rate_limiter.check_key(&key) {
Ok(_) => Ok(next.run(req).await),
Err(_) => Err(AppError::RateLimited),
}
}4.3 限流参数
rust
// CubeAPI/src/state.rs
let rate_limiter = RateLimiter::keyed(
Quota::per_minute(NonZeroU32::new(60).unwrap()) // 60 次/分钟
);5. 错误处理:AppError
CubeAPI/src/error/mod.rs 定义了统一的错误类型:
5.1 AppError 枚举
rust
pub enum AppError {
BadRequest(String),
Unauthorized,
NotFound(String),
RateLimited,
Internal(String),
CubeMaster(CubeMasterClientError),
Timeout,
}5.2 错误映射
5.3 IntoResponse 实现
rust
// CubeAPI/src/error/mod.rs
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self {
Self::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
Self::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized".into()),
Self::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
Self::RateLimited => (StatusCode::TOO_MANY_REQUESTS, "Rate limited".into()),
Self::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
Self::CubeMaster(e) => (StatusCode::BAD_GATEWAY, e.to_string()),
Self::Timeout => (StatusCode::GATEWAY_TIMEOUT, "Timeout".into()),
};
(status, Json(json!({ "error": message }))).into_response()
}
}6. 完整中间件栈执行顺序
延伸阅读
- CubeAPI 是怎么接住第一个请求的 — 路由注册与 AppState
- E2B 兼容接口如何映射到内部模型 — 字段映射细节