Skip to content

中间件、鉴权与错误处理

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/validate

4. 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. 完整中间件栈执行顺序


延伸阅读