You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 02:57:22