自定义中间件

中间件结构

自定义中间件需要实现 MiddleWareHandler trait:

use silent::prelude::*;

struct MyMiddleware;

#[async_trait]
impl MiddleWareHandler for MyMiddleware {
    async fn handle(&self, req: Request, next: &Next) -> Result<Response> {
        // 前置处理
        println!("Before request: {}", req.uri());
        
        // 调用下一个中间件或处理器
        let res = next.call(req).await?;
        
        // 后置处理
        println!("After request: {}", res.status());
        
        Ok(res)
    }
}

中间件生命周期

中间件在请求处理过程中遵循以下生命周期:

  1. 前置处理:在请求到达下一个处理器之前执行

  2. 调用下一个处理器:通过 next.call(req) 传递请求

  3. 后置处理:在获得响应后执行

  4. 返回处理后的响应

示例:请求计时中间件

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

struct TimingMiddleware;

#[async_trait]
impl MiddleWareHandler for TimingMiddleware {
    async fn handle(&self, req: Request, next: &Next) -> Result<Response> {
        let start = Instant::now();
        let res = next.call(req).await?;
        let duration = start.elapsed();
        
        println!("Request took: {:?}", duration);
        
        Ok(res)
    }
}

注册使用

let route = Route::new("")
    .hook(TimingMiddleware)
    .get(handler);

中间件执行顺序

中间件按照注册顺序形成处理链,遵循"洋葱模型":

  1. 请求按注册顺序从外到内经过每个中间件的前置处理

  2. 到达最终的处理函数

  3. 响应按相反顺序从内到外经过每个中间件的后置处理

let route = Route::new("")
    .hook(LogMiddleware)    // 最先执行前置处理,最后执行后置处理
    .hook(AuthMiddleware)   // 第二个执行前置处理,倒数第二个执行后置处理
    .hook(TimingMiddleware) // 最后执行前置处理,最先执行后置处理
    .get(handler);         // 处理函数

最佳实践

  1. 保持中间件职责单一,每个中间件只负责一个特定功能

  2. 避免在中间件中进行耗时操作,可能会影响整个请求的响应时间

  3. 正确处理错误,使用 Result 类型返回错误信息

  4. 合理安排中间件执行顺序,将通用处理(如日志)放在外层,特定处理(如认证)放在内层

  5. 注意中间件的状态管理,如果需要共享状态,确保使用适当的同步机制

  6. 在开发中间件时注意性能影响,可以通过性能分析工具评估中间件的开销

Last updated

Was this helpful?