路由守卫 #
什么是路由守卫? #
路由守卫是一种机制,用于在请求到达处理函数之前进行检查。守卫可以决定是否允许请求继续处理,或者返回错误响应。
text
┌─────────────────────────────────────────────────────────────┐
│ 守卫执行流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ HTTP 请求 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 路由守卫 │ │
│ │ 检查请求方法、头部、参数等 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ ▼ ▼ │
│ 通过 拒绝 │
│ │ │ │
│ ▼ ▼ │
│ 处理函数 错误响应 │
│ │
└─────────────────────────────────────────────────────────────┘
内置守卫 #
方法守卫 #
rust
use actix_web::{guard, web, App, HttpResponse, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route(
"/users",
web::route()
.guard(guard::Get())
.to(get_users)
)
.route(
"/users",
web::route()
.guard(guard::Post())
.to(create_user)
)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
async fn get_users() -> HttpResponse {
HttpResponse::Ok().json(vec!["Alice", "Bob"])
}
async fn create_user() -> HttpResponse {
HttpResponse::Created().body("User created")
}
头部守卫 #
rust
use actix_web::guard::Header;
App::new()
.route(
"/api",
web::route()
.guard(Header("content-type", "application/json"))
.to(api_handler)
)
.route(
"/admin",
web::route()
.guard(Header("X-Admin-Key", "secret123"))
.to(admin_handler)
)
Get 守卫 #
rust
App::new()
.route(
"/",
web::route()
.guard(guard::Get())
.to(index)
)
Not 守卫 #
rust
App::new()
.route(
"/api",
web::route()
.guard(guard::Not(guard::Get()))
.to(api_handler)
)
组合守卫 #
rust
App::new()
.route(
"/api",
web::route()
.guard(guard::All(guard::Get()).and(Header("X-Api-Key", "secret")))
.to(api_handler)
)
.route(
"/public",
web::route()
.guard(guard::Any(guard::Get()).or(guard::Post()))
.to(public_handler)
)
自定义守卫 #
实现 Guard Trait #
rust
use actix_web::{guard::Guard, guard::GuardContext, http::header};
struct ContentTypeJson;
impl Guard for ContentTypeJson {
fn check(&self, ctx: &GuardContext) -> bool {
ctx.head()
.headers()
.get(header::CONTENT_TYPE)
.map(|v| v == "application/json")
.unwrap_or(false)
}
}
App::new()
.route(
"/api",
web::route()
.guard(ContentTypeJson)
.to(api_handler)
)
带参数的守卫 #
rust
struct HeaderGuard {
name: &'static str,
value: &'static str,
}
impl HeaderGuard {
fn new(name: &'static str, value: &'static str) -> Self {
Self { name, value }
}
}
impl Guard for HeaderGuard {
fn check(&self, ctx: &GuardContext) -> bool {
ctx.head()
.headers()
.get(self.name)
.map(|v| v == self.value)
.unwrap_or(false)
}
}
App::new()
.route(
"/admin",
web::route()
.guard(HeaderGuard::new("X-Admin-Key", "secret"))
.to(admin_handler)
)
认证守卫 #
rust
struct AuthGuard {
expected_token: String,
}
impl AuthGuard {
fn new(token: &str) -> Self {
Self {
expected_token: token.to_string(),
}
}
}
impl Guard for AuthGuard {
fn check(&self, ctx: &GuardContext) -> bool {
ctx.head()
.headers()
.get(header::AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.map(|v| v == format!("Bearer {}", self.expected_token))
.unwrap_or(false)
}
}
App::new()
.route(
"/protected",
web::route()
.guard(AuthGuard::new("my-secret-token"))
.to(protected_handler)
)
IP 地址守卫 #
rust
use std::net::IpAddr;
struct IpWhitelistGuard {
allowed_ips: Vec<IpAddr>,
}
impl IpWhitelistGuard {
fn new(ips: Vec<&str>) -> Self {
Self {
allowed_ips: ips
.into_iter()
.filter_map(|ip| ip.parse().ok())
.collect(),
}
}
}
impl Guard for IpWhitelistGuard {
fn check(&self, ctx: &GuardContext) -> bool {
ctx.head()
.peer_addr()
.map(|addr| self.allowed_ips.contains(&addr.ip()))
.unwrap_or(false)
}
}
App::new()
.route(
"/admin",
web::route()
.guard(IpWhitelistGuard::new(vec!["127.0.0.1", "192.168.1.100"]))
.to(admin_handler)
)
守卫工厂函数 #
创建可复用守卫 #
rust
fn admin_guard() -> impl Guard {
guard::All(guard::Get())
.and(Header("X-Admin-Key", "secret"))
}
App::new()
.route("/admin", web::route().guard(admin_guard()).to(admin_handler))
.route("/admin/users", web::route().guard(admin_guard()).to(list_users))
动态守卫 #
rust
fn api_key_guard(api_key: &str) -> impl Guard + '_ {
move |ctx: &GuardContext| {
ctx.head()
.headers()
.get("X-Api-Key")
.map(|v| v == api_key)
.unwrap_or(false)
}
}
范围守卫 #
Scope 级别守卫 #
rust
App::new()
.service(
web::scope("/api")
.guard(guard::Header("X-Api-Version", "1"))
.route("/users", web::get().to(get_users))
.route("/posts", web::get().to(get_posts))
)
Resource 级别守卫 #
rust
App::new()
.service(
web::resource("/admin")
.guard(Header("X-Admin-Key", "secret"))
.route(web::get().to(admin_panel))
.route(web::post().to(admin_action))
)
守卫与中间件对比 #
| 特性 | 守卫 | 中间件 |
|---|---|---|
| 执行时机 | 路由匹配前 | 路由匹配后 |
| 主要用途 | 请求过滤 | 请求/响应处理 |
| 返回值 | bool | Response |
| 可修改请求 | 否 | 是 |
| 可修改响应 | 否 | 是 |
完整示例 #
rust
use actix_web::{
guard::{Guard, GuardContext},
web, App, HttpResponse, HttpServer, Responder,
};
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct User {
id: u32,
name: String,
}
struct AuthGuard;
impl Guard for AuthGuard {
fn check(&self, ctx: &GuardContext) -> bool {
ctx.head()
.headers()
.get("Authorization")
.and_then(|v| v.to_str().ok())
.map(|v| v.starts_with("Bearer "))
.unwrap_or(false)
}
}
struct AdminGuard;
impl Guard for AdminGuard {
fn check(&self, ctx: &GuardContext) -> bool {
ctx.head()
.headers()
.get("X-Admin-Key")
.map(|v| v == "admin-secret")
.unwrap_or(false)
}
}
#[actix_web::get("/users")]
async fn list_users() -> impl Responder {
HttpResponse::Ok().json(vec![
User { id: 1, name: "Alice".to_string() },
User { id: 2, name: "Bob".to_string() },
])
}
#[actix_web::get("/profile")]
async fn profile() -> impl Responder {
HttpResponse::Ok().json(User {
id: 1,
name: "Current User".to_string(),
})
}
#[actix_web::get("/admin/users")]
async fn admin_list_users() -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"users": [
{"id": 1, "name": "Alice", "role": "user"},
{"id": 2, "name": "Bob", "role": "admin"}
]
}))
}
#[actix_web::post("/admin/users")]
async fn admin_create_user(body: web::Json<CreateUser>) -> impl Responder {
HttpResponse::Created().json(body.into_inner())
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(list_users)
.service(
web::scope("/protected")
.guard(AuthGuard)
.service(profile)
)
.service(
web::scope("/admin")
.guard(AdminGuard)
.service(admin_list_users)
.service(admin_create_user)
)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
测试守卫 #
bash
# 普通请求
curl http://localhost:8080/users
# 需要认证的请求
curl -H "Authorization: Bearer token" http://localhost:8080/protected/profile
# 需要管理员权限的请求
curl -H "X-Admin-Key: admin-secret" http://localhost:8080/admin/users
守卫最佳实践 #
1. 单一职责 #
每个守卫只检查一个条件:
rust
struct MethodGuard;
struct AuthGuard;
struct AdminGuard;
2. 组合使用 #
使用 All 和 Any 组合守卫:
rust
guard::All(AuthGuard).and(AdminGuard)
3. 错误处理 #
守卫失败时返回适当的错误:
rust
App::new()
.default_service(
web::route()
.guard(guard::Not(guard::Get()))
.to(|| HttpResponse::MethodNotAllowed())
)
下一步 #
现在你已经掌握了路由守卫,继续学习 请求参数,深入了解请求处理!
最后更新:2026-03-29