中间件

中间件是 Silent 框架中的重要组成部分,它允许你在请求处理过程中插入自定义逻辑。本文将详细介绍中间件的工作原理、使用方法和最佳实践。

中间件工作原理

在 Silent 中,中间件采用洋葱模型(Onion Model)工作。Next 结构体通过链式结构实现了中间件的调用流程。Next 结构体的核心实现包括:

pub struct Next {
    inner: NextInstance,
    next: Option<Arc<Next>>,
}

pub(crate) enum NextInstance {
    Middleware(Arc<dyn MiddleWareHandler>),
    EndPoint(Arc<dyn Handler>),
}

中间件的执行流程如下:

  1. 请求进入时,通过Next::build方法构建中间件链:

    • 首先创建包含终端处理器(EndPoint)的Next实例,作为链的终点

    • 然后从后向前遍历中间件列表,将每个中间件包装为Next实例

    • 每个Next实例通过Arc智能指针持有下一个节点的引用,形成链式结构

    • build方法的实现确保了中间件的执行顺序与注册顺序一致

  2. 执行时,从最外层中间件开始:

    • 中间件通过实现MiddleWareHandler特征的handle方法处理请求

    • handle方法接收当前请求(Request)和下一个中间件(Next)作为参数

    • 通过next.call()调用链中的下一个节点,实现请求的传递

    • 可以在next.call()前后添加自定义逻辑,实现请求和响应的处理

  3. 当请求到达终端处理器时:

    • 通过Handler特征的call方法处理请求并生成响应

    • 响应沿着中间件链原路返回,实现洋葱模型

    • 每个中间件都可以对响应进行处理,例如添加响应头或修改响应内容

    • 最终响应返回给客户端

     │     请求      │
     ▼              │
Logger中间件         │
     │              │
     ▼              │
  认证中间件         │   响应
     │              │
     ▼              │
  业务处理器         │
     │              ▲
     └──────────────┘

中间件定义

在 Silent 中,中间件是一个实现了 MiddleWareHandler trait 的结构体:

use silent::prelude::*;

pub struct Logger;

#[async_trait]
impl MiddleWareHandler for Logger {
    async fn handle(&self, req: Request, next: &Next) -> Result<Response> {
        println!("Request: {} {}", req.method(), req.uri());
        let start = std::time::Instant::now();
        
        // 调用下一个中间件或处理器
        let response = next.call(req).await?;
        
        println!("Response time: {:?}", start.elapsed());
        Ok(response)
    }
}

中间件生命周期

  1. 前置处理:在 next.call() 之前的代码

    • 可以检查和修改请求

    • 可以提前返回响应(如认证失败)

    • 可以添加请求上下文

  2. 后置处理:在 next.call() 之后的代码

    • 可以修改响应

    • 可以记录响应时间

    • 可以进行清理工作

中间件注册

你可以在路由级别注册中间件:

use silent::prelude::*;

// 创建路由并注册中间件
let route = Route::new("")
    .hook(Logger::new())
    .hook(Authentication::new())
    .get(|_req| async { Ok("Hello World") });

// 子路由中间件
let admin = Route::new("admin")
    .hook(RequireAdmin::new())
    .get(admin_dashboard);

中间件的注册顺序决定了它们的执行顺序。在上面的例子中,请求会先经过Logger中间件,然后是Authentication中间件,最后到达处理函数。

// 全局中间件
let app = Route::new("")
    .middleware(Logger)
    .middleware(Authentication)
    .at(api_routes);

// 路由级别中间件
let protected = Route::new("admin")
    .middleware(RequireAdmin)
    .get(admin_dashboard);

常见中间件示例

认证中间件

pub struct Auth;

#[async_trait]
impl MiddleWareHandler for Auth {
    async fn handle(&self, mut req: Request, next: &Next) -> Result<Response> {
        let token = req.headers()
            .get("Authorization")
            .ok_or_else(|| Error::new("Missing token")
                .with_status(StatusCode::UNAUTHORIZED))?;
        
        // 验证 token
        if !verify_token(token) {
            return Err(Error::new("Invalid token")
                .with_status(StatusCode::UNAUTHORIZED));
        }
        
        next.call(req).await
    }
}

CORS 中间件

pub struct Cors;

#[async_trait]
impl MiddleWareHandler for Cors {
    async fn handle(&self, req: Request, next: &Next) -> Result<Response> {
        let response = next.call(req).await?;
        
        Ok(response.builder()
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
            .header("Access-Control-Allow-Headers", "Content-Type")
            .build()?)
    }
}

请求限流中间件

use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};

pub struct RateLimit {
    count: AtomicUsize,
    last_reset: std::sync::Mutex<Instant>,
    limit: usize,
    window: Duration,
}

impl RateLimit {
    pub fn new(limit: usize, window: Duration) -> Self {
        Self {
            count: AtomicUsize::new(0),
            last_reset: std::sync::Mutex::new(Instant::now()),
            limit,
            window,
        }
    }
}

#[async_trait]
impl MiddleWareHandler for RateLimit {
    async fn handle(&self, req: Request, next: &Next) -> Result<Response> {
        let mut last_reset = self.last_reset.lock().unwrap();
        if last_reset.elapsed() >= self.window {
            self.count.store(0, Ordering::SeqCst);
            *last_reset = Instant::now();
        }
        
        if self.count.fetch_add(1, Ordering::SeqCst) >= self.limit {
            return Err(Error::new("Too many requests")
                .with_status(StatusCode::TOO_MANY_REQUESTS));
        }
        
        next.call(req).await
    }
}

中间件链

多个中间件按照注册顺序依次执行:

let app = Route::new("")
    .middleware(Logger)       // 1. 记录请求
    .middleware(Cors)        // 2. 处理跨域
    .middleware(RateLimit::new(100, Duration::from_secs(60))) // 3. 限流
    .at(Route::new("api")
        .middleware(Auth)    // 4. API 认证
        .at(api_routes)
    );

最佳实践

性能优化

  1. 避免阻塞操作

    • 使用异步 IO 操作

    • 将耗时操作放入专门的线程池

    • 合理使用缓存

  2. 减少内存分配

    • 复用请求和响应对象

    • 避免不必要的克隆

    • 使用引用而不是拥有权

  3. 优化中间件顺序

    • 将频繁使用的中间件放在前面

    • 将耗时的中间件放在必要的位置

错误处理

  1. 合理使用错误类型

    • 使用自定义错误类型

    • 提供详细的错误信息

    • 正确设置 HTTP 状态码

  2. 优雅降级

    • 提供默认值

    • 实现重试机制

    • 记录错误日志

安全性

  1. 输入验证

    • 验证所有用户输入

    • 防止 SQL 注入

    • 防止 XSS 攻击

  2. 敏感信息处理

    • 加密敏感数据

    • 不在日志中记录敏感信息

    • 使用安全的会话管理

示例:完整的中间件应用

这是一个包含多个中间件的完整示例:

use silent::prelude::*;
use std::time::Duration;

// 应用配置
let config = AppConfig {
    cors_origin: "*".to_string(),
    rate_limit: 100,
    rate_window: Duration::from_secs(60),
};

// API 路由
let api = Route::new("api")
    .middleware(Auth)  // API 认证
    .at(Route::new("users").get(list_users))
    .at(Route::new("posts").get(list_posts));

// 管理员路由
let admin = Route::new("admin")
    .middleware(RequireAdmin)  // 管理员权限
    .at(Route::new("dashboard").get(admin_dashboard));

// 应用程序
let app = Route::new("")
    .middleware(Logger)  // 请求日志
    .middleware(Cors::new(config.cors_origin))  // CORS
    .middleware(RateLimit::new(  // 限流
        config.rate_limit,
        config.rate_window,
    ))
    .at(api)
    .at(admin);

这个示例展示了如何组织一个包含日志记录、跨域处理、限流、认证和权限控制等功能的完整应用程序。中间件的组织结构清晰,便于维护和扩展。

Last updated

Was this helpful?