如何在Rocket.rs中正确编写请求守卫?遇类型不兼容错误
问题
我尝试编写一个请求守卫,使其要么返回user_id: String,要么直接以Status::Unauthorized终止请求。以下是我的代码:
use rocket::async_trait; use rocket::http::Status; use rocket::outcome::IntoOutcome; use rocket::request::{FromRequest, Outcome, Request}; use mongodb::bson::doc; // modules use crate::user_module::user_model::UserModel; use crate::DB; struct Auth {} #[async_trait] impl<'r> FromRequest<'r> for Auth { type Error = (); async fn from_request(req: &'r Request<'_>) -> Outcome<String, ()> { let db = DB.get().unwrap(); let users_collection = db.collection::<UserModel>("users"); req.cookies() .get_private("user_id") .and_then(|cookie| cookie.value().parse::<String>().ok()) .or_forward(Status::Unauthorized) } }
无论我如何修改,始终会报错。当前的错误信息为:
method `from_request` has an incompatible type for trait expected signature `fn(&'r rocket::Request<'_>) -> Pin<Box<(dyn std::future::Future<Output = Outcome<guards::Auth::Auth, (Status, ()), Status>> + std::marker::Send + 'async_trait)>>` found signature `fn(&'r rocket::Request<'_>) -> Pin<Box<(dyn std::future::Future<Output = Outcome<std::string::String, (Status, ()), Status>> + std::marker::Send + 'async_trait)>>`
我完全不清楚这是什么问题,而且我找到的指南似乎不完整,比如相关的struct在哪里?
解决方案
核心问题
你实现FromRequest trait时,关联类型和返回值完全不匹配:
- 你为
Auth结构体实现FromRequest,但trait要求from_request返回Outcome<Auth, Self::Error>(即Outcome<Auth, ()>),而你实际返回的是Outcome<String, ()>,类型对应错误。 - 错误信息里的预期签名明确显示需要返回
Outcome<guards::Auth::Auth, ...>,而你返回的是Outcome<String, ...>,这就是报错的根源。
修复步骤
根据你的需求(返回user_id: String或终止请求),有两种可行方案:
方案1:让Auth结构体持有user_id字段
把空的Auth结构体改成带数据的结构,让它作为守卫的返回值:
use rocket::async_trait; use rocket::http::Status; use rocket::outcome::IntoOutcome; use rocket::request::{FromRequest, Outcome, Request}; use mongodb::bson::doc; // modules use crate::user_module::user_model::UserModel; use crate::DB; // 让Auth结构体持有user_id字段,作为守卫的返回载体 struct Auth { user_id: String, } #[async_trait] impl<'r> FromRequest<'r> for Auth { type Error = (); async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> { // 若后续需要验证用户存在,可在这里补充数据库查询逻辑 // let db = DB.get().unwrap(); // let users_collection = db.collection::<UserModel>("users"); req.cookies() .get_private("user_id") .and_then(|cookie| cookie.value().parse::<String>().ok()) // 将解析出的user_id包装成Auth实例,再转换为符合trait要求的Outcome .map(|user_id| Auth { user_id }) .or_forward(Status::Unauthorized) } }
后续在路由中使用该守卫时,可通过auth.user_id获取用户ID:
#[get("/protected")] async fn protected_route(auth: Auth) -> String { format!("当前用户ID:{}", auth.user_id) }
方案2:直接为String实现FromRequest
如果不需要额外封装,只想直接返回user_id: String作为守卫值,可以跳过Auth结构体,直接为String实现FromRequest:
use rocket::async_trait; use rocket::http::Status; use rocket::outcome::IntoOutcome; use rocket::request::{FromRequest, Outcome, Request}; use mongodb::bson::doc; // modules use crate::user_module::user_model::UserModel; use crate::DB; #[async_trait] impl<'r> FromRequest<'r> for String { type Error = (); async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> { // 若需要验证用户存在,可在这里补充数据库查询逻辑 // let db = DB.get().unwrap(); // let users_collection = db.collection::<UserModel>("users"); req.cookies() .get_private("user_id") .and_then(|cookie| cookie.value().parse::<String>().ok()) .or_forward(Status::Unauthorized) } }
使用时直接在路由参数中接收String类型的用户ID:
#[get("/protected")] async fn protected_route(user_id: String) -> String { format!("当前用户ID:{}", user_id) }
额外补充:验证用户存在的逻辑
你代码中获取了users_collection但未使用,如果需求是不仅提取user_id,还要验证该用户确实存在于数据库中,可以在from_request里补充查询逻辑(以方案1为例):
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> { let db = DB.get().unwrap(); let users_collection = db.collection::<UserModel>("users"); // 先提取并解析user_id,失败则直接返回Unauthorized let user_id = match req.cookies() .get_private("user_id") .and_then(|cookie| cookie.value().parse::<String>().ok()) { Some(id) => id, None => return Outcome::Forward(Status::Unauthorized), }; // 查询数据库验证用户是否存在 if users_collection.find_one(doc! {"_id": &user_id}, None).await.unwrap().is_some() { Outcome::Success(Auth { user_id }) } else { Outcome::Forward(Status::Unauthorized) } }
内容的提问来源于Stack Exchange,提问作者Kristiano Odadu
相关产品推荐
相关产品推荐

