Fairings中间件 #

Fairings是Rocket的中间件系统,允许在请求生命周期的各个阶段执行代码。它们类似于Express.js中的中间件或Django中的中间件。

Fairing概述 #

生命周期钩子 #

钩子 触发时机 用途
on_attach 附加到Rocket实例时 配置验证、初始化
on_launch Rocket启动时 资源初始化
on_request 请求到达时 请求预处理、日志
on_response 响应发送前 响应修改、日志

Fairing类型 #

rust
use rocket::fairing::{Fairing, Info, Kind};

struct MyFairing;

#[rocket::async_trait]
impl Fairing for MyFairing {
    fn info(&self) -> Info {
        Info {
            name: "My Fairing",
            kind: Kind::Request | Kind::Response,
        }
    }
}

请求日志Fairing #

基本日志 #

rust
use rocket::{Request, Data, Response};
use rocket::fairing::{Fairing, Info, Kind};

pub struct RequestLogger;

#[rocket::async_trait]
impl Fairing for RequestLogger {
    fn info(&self) -> Info {
        Info {
            name: "Request Logger",
            kind: Kind::Request,
        }
    }

    async fn on_request(&self, request: &mut Request<'_>, _data: &mut Data<'_>) {
        println!(
            "[{}] {} {}",
            chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
            request.method(),
            request.uri()
        );
    }
}

详细日志 #

rust
use rocket::{Request, Data, Response};
use rocket::fairing::{Fairing, Info, Kind};

pub struct DetailedLogger;

#[rocket::async_trait]
impl Fairing for DetailedLogger {
    fn info(&self) -> Info {
        Info {
            name: "Detailed Logger",
            kind: Kind::Request | Kind::Response,
        }
    }

    async fn on_request(&self, request: &mut Request<'_>, _data: &mut Data<'_>) {
        let headers: Vec<_> = request.headers().iter()
            .map(|h| format!("{}: {}", h.name(), h.value()))
            .collect();
        
        println!(
            ">>> {} {}\nHeaders: {:?}",
            request.method(),
            request.uri(),
            headers
        );
    }

    async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
        println!("<<< Status: {}", response.status());
    }
}

响应头Fairing #

添加安全头 #

rust
use rocket::{Request, Response};
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::Header;

pub struct SecurityHeaders;

#[rocket::async_trait]
impl Fairing for SecurityHeaders {
    fn info(&self) -> Info {
        Info {
            name: "Security Headers",
            kind: Kind::Response,
        }
    }

    async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
        response.set_header(Header::new("X-Content-Type-Options", "nosniff"));
        response.set_header(Header::new("X-Frame-Options", "DENY"));
        response.set_header(Header::new("X-XSS-Protection", "1; mode=block"));
        response.set_header(Header::new("Strict-Transport-Security", "max-age=31536000"));
    }
}

CORS Fairing #

rust
use rocket::{Request, Response};
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::Header;

pub struct Cors;

#[rocket::async_trait]
impl Fairing for Cors {
    fn info(&self) -> Info {
        Info {
            name: "CORS",
            kind: Kind::Response,
        }
    }

    async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
        let origin = request.headers()
            .get_one("Origin")
            .unwrap_or("*");
        
        response.set_header(Header::new("Access-Control-Allow-Origin", origin));
        response.set_header(Header::new("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"));
        response.set_header(Header::new("Access-Control-Allow-Headers", "Content-Type, Authorization"));
        response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
    }
}

请求计时Fairing #

rust
use rocket::{Request, Data, Response};
use rocket::fairing::{Fairing, Info, Kind};
use std::time::Instant;

pub struct Timer;

#[rocket::async_trait]
impl Fairing for Timer {
    fn info(&self) -> Info {
        Info {
            name: "Request Timer",
            kind: Kind::Request | Kind::Response,
        }
    }

    async fn on_request(&self, request: &mut Request<'_>, _data: &mut Data<'_>) {
        let start = Instant::now();
        request.local_cache(|| start);
    }

    async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
        if let Some(start) = request.local_cache::<Instant>().get() {
            let duration = start.elapsed();
            let ms = duration.as_secs_f64() * 1000.0;
            println!("Request took {:.2}ms", ms);
            response.set_header(Header::new("X-Response-Time", format!("{:.2}ms", ms)));
        }
    }
}

初始化Fairing #

on_attach #

rust
use rocket::Rocket;
use rocket::fairing::{Fairing, Info, Kind};

pub struct DatabaseInit;

#[rocket::async_trait]
impl Fairing for DatabaseInit {
    fn info(&self) -> Info {
        Info {
            name: "Database Initialization",
            kind: Kind::Attach,
        }
    }

    async fn on_attach(&self, rocket: Rocket<rocket::Build>) -> Result<Rocket<rocket::Build>, Rocket<rocket::Build>> {
        println!("Initializing database connection...");
        Ok(rocket)
    }
}

on_launch #

rust
use rocket::Rocket;
use rocket::fairing::{Fairing, Info, Kind};

pub struct StartupLogger;

#[rocket::async_trait]
impl Fairing for StartupLogger {
    fn info(&self) -> Info {
        Info {
            name: "Startup Logger",
            kind: Kind::Launch,
        }
    }

    async fn on_launch(&self, _rocket: &Rocket<rocket::Orbit>) {
        println!("🚀 Rocket has launched!");
    }
}

速率限制Fairing #

rust
use rocket::{Request, Data, Response};
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::Status;
use std::collections::HashMap;
use std::sync::Mutex;
use std::time::{Duration, Instant};

pub struct RateLimiter {
    requests: Mutex<HashMap<String, Vec<Instant>>>,
    max_requests: usize,
    window: Duration,
}

impl RateLimiter {
    pub fn new(max_requests: usize, window_secs: u64) -> Self {
        Self {
            requests: Mutex::new(HashMap::new()),
            max_requests,
            window: Duration::from_secs(window_secs),
        }
    }
}

#[rocket::async_trait]
impl Fairing for RateLimiter {
    fn info(&self) -> Info {
        Info {
            name: "Rate Limiter",
            kind: Kind::Request,
        }
    }

    async fn on_request(&self, request: &mut Request<'_>, _data: &mut Data<'_>) {
        let ip = request.client_ip()
            .map(|ip| ip.to_string())
            .unwrap_or_else(|| "unknown".to_string());
        
        let now = Instant::now();
        let mut requests = self.requests.lock().unwrap();
        
        let entry = requests.entry(ip).or_insert_with(Vec::new);
        entry.retain(|&t| now.duration_since(t) < self.window);
        
        if entry.len() >= self.max_requests {
            println!("Rate limit exceeded");
        } else {
            entry.push(now);
        }
    }
}

完整示例 #

rust
#[macro_use] extern crate rocket;

use rocket::{Request, Data, Response};
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::Header;
use std::time::Instant;

struct Logger;

#[rocket::async_trait]
impl Fairing for Logger {
    fn info(&self) -> Info {
        Info {
            name: "Request Logger",
            kind: Kind::Request | Kind::Response,
        }
    }

    async fn on_request(&self, request: &mut Request<'_>, _data: &mut Data<'_>) {
        let start = Instant::now();
        request.local_cache(|| start);
        println!(">>> {} {}", request.method(), request.uri());
    }

    async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
        if let Some(start) = request.local_cache::<Instant>().get() {
            let duration = start.elapsed();
            println!("<<< {} ({:.2}ms)", response.status(), duration.as_secs_f64() * 1000.0);
        }
    }
}

struct SecurityHeaders;

#[rocket::async_trait]
impl Fairing for SecurityHeaders {
    fn info(&self) -> Info {
        Info {
            name: "Security Headers",
            kind: Kind::Response,
        }
    }

    async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
        response.set_header(Header::new("X-Content-Type-Options", "nosniff"));
        response.set_header(Header::new("X-Frame-Options", "DENY"));
    }
}

#[get("/")]
fn index() -> &'static str {
    "Hello, World!"
}

#[get("/slow")]
async fn slow() -> &'static str {
    rocket::tokio::time::sleep(std::time::Duration::from_millis(100)).await;
    "Slow response"
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .attach(Logger)
        .attach(SecurityHeaders)
        .mount("/", routes![index, slow])
}

下一步 #

掌握了Fairings中间件后,让我们继续学习 状态管理,了解如何在Rocket中管理应用状态。

最后更新:2026-03-28