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