在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
相关产品推荐
相关产品推荐

