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

在Rust的Warp框架中如何在过滤器中使用提取的路径参数?

问题描述

我有格式为/entity1/<id>/entity2/<id>/stuff的端点,示例代码如下:

let route = warp::path("entity1")
        .and(warp::path::param::<u64>())
        .and(warp::path("entity2"))
        .and(warp::path::param::<u64>())
        .and(warp::path("stuff"))
        .and(warp::path::end())
        .and(with_auth(db.clone())) // 通过Authorization头验证用户身份
        .and_then(handler_function);

我有多个类似的端点,这些端点需要从数据库加载entity2,若其与entity1不匹配则拒绝请求,之后再对加载的实体进行操作。我的想法是创建一个过滤器,通过.and(with_entity2(db.clone()))的方式从数据库加载实体并传递给处理函数(同时进行验证,必要时拒绝请求),示例代码如下:

pub fn with_entity2(db: &Db) -> impl Filter<Extract = (Entity2,), Error = warp::Rejection> + Clone {
  // 在此处获取路径参数?
}

我猜测可以使用FullPath,但需要自行解析。请问是否可以直接获取路径参数?或者有更好的方案?我不再需要已提取的参数,但希望在用户认证通过且路径匹配完成后再执行数据库查询。

补充说明:以下是更完整的实现示例:

warp::path!("entity1" / u64 / "entity2" / u64)
    .and(warp::get())
    .and(with_auth())
    .and(with_entity2())
    .and_then(handler_func);

pub fn with_entity2() -> impl Filter<Extract = (Entity2,), Error = warp::Rejection> + Clone {
    warp::filters::path::full()
        .and_then(get_entity_2)
}

async fn check_match(path: FullPath) -> Result<Entity2, warp::Rejection> {
    let entity1_id = ??;
    let entity2_id = ??;
    match Entity2::find_by_id(entity2_id).await {
        Ok(entity2) => {
            if entity2.fk_entity1 == entity1_id {
                Ok(entity2)
            } else {
                Err(warp::reject::not_found())
            }
        }
        Err(e) => Err(warp::reject::not_found()),
    }
}
解决方案

不需要手动解析FullPath,Warp支持直接将已提取的路径参数传递给自定义过滤器,既不用重复解析路径,也能保证参数获取的准确性。

方法一:直接传递路径参数给with_entity2

调整路由定义,让前面提取的entity1_id和entity2_id自动传入with_entity2过滤器,同时传入数据库连接:

// 路由定义
warp::path!("entity1" / u64 / "entity2" / u64)
    .and(warp::get())
    .and(with_auth())
    .and(with_entity2(db.clone()))
    .and_then(handler_func);

// 实现with_entity2过滤器
pub fn with_entity2(db: Db) -> impl Filter<Extract = (Entity2,), Error = warp::Rejection> + Clone {
    warp::any().and_then(move |entity1_id: u64, entity2_id: u64| {
        let db = db.clone();
        async move {
            match Entity2::find_by_id(&db, entity2_id).await {
                Ok(entity2) => {
                    if entity2.fk_entity1 == entity1_id {
                        Ok(entity2)
                    } else {
                        Err(warp::reject::not_found())
                    }
                }
                Err(_) => Err(warp::reject::not_found()),
            }
        }
    })
}

方法二:封装通用关联验证过滤器

如果多个端点需要类似的实体关联验证逻辑,可以封装通用过滤器复用代码:

// 通用过滤器:验证entity2属于entity1
pub fn validate_entity_belongs_to_entity1(
    db: Db,
) -> impl Filter<Extract = (Entity2,), Error = warp::Rejection> + Clone {
    warp::any().and_then(move |entity1_id: u64, entity2_id: u64| {
        let db = db.clone();
        async move {
            Entity2::find_by_id(&db, entity2_id)
                .await
                .map_err(|_| warp::reject::not_found())
                .and_then(|entity2| {
                    if entity2.fk_entity1 == entity1_id {
                        Ok(entity2)
                    } else {
                        Err(warp::reject::not_found())
                    }
                })
        }
    })
}

// 路由使用示例
warp::path!("entity1" / u64 / "entity2" / u64)
    .and(warp::get())
    .and(with_auth())
    .and(validate_entity_belongs_to_entity1(db.clone()))
    .and_then(handler_func);

关键说明

  • 利用Warp过滤器的组合特性,前面路径提取的参数会自动传递给后续.and_then闭包的参数列表,无需手动解析路径。
  • 数据库查询在认证通过后执行,完全符合需求。
  • 这种方式比解析FullPath更可靠,避免了手动解析路径可能出现的错误,也契合Warp的设计理念。

内容的提问来源于stack exchange,提问作者BlueMonkey325

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:17:03