当前位置: 首页 > news >正文

Rust:warp 编写 RESTful API 的示例及原理分析

1. 源代码

不多说,直接贴代码:

Cargo.toml

[package]
name = "restfultest"
version = "0.1.0"
edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
warp = "0.3"  
tokio = { version = "1", features = ["full"] }
crossbeam = "0.8.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = { version = "0.4", features = ["serde"] }
rand = "0.8.5"
lazy_static = "1.4"

main.rs

use lazy_static::lazy_static;
use std::collections::HashMap;
use warp::Filter;
use std::sync::Mutex;// 模拟数据库
lazy_static! {static ref DB: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
}#[tokio::main]
async fn main() {// 定义路由let routes = warp::get().and(warp::path!("items" / String)).and_then(get_item).or(warp::post().and(warp::path("items")).and(warp::body::json()).and_then(create_item)).or(warp::put().and(warp::path!("items" / String)).and(warp::body::json()).and_then(update_item)).or(warp::delete().and(warp::path!("items" / String)).and_then(delete_item));// 启动服务器warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
}// 处理 GET 请求,获取指定项目
async fn get_item(id: String) -> Result<impl warp::Reply, warp::Rejection> {let db = DB.lock().unwrap();match db.get(&id) {Some(item) => Ok(warp::reply::json(&item)),None => Err(warp::reject::not_found()),}
}// 处理 POST 请求,创建新项目
async fn create_item(item: HashMap<String, String>) -> Result<impl warp::Reply, warp::Rejection> {let mut db = DB.lock().unwrap();let id = item.get("id").unwrap_or(&String::new()).to_string();db.insert(id, item.get("value").unwrap_or(&String::new()).to_string());Ok(warp::reply::json(&item))
}// 处理 PUT 请求,更新指定项目
async fn update_item(id: String,item: HashMap<String, String>,
) -> Result<impl warp::Reply, warp::Rejection> {let mut db = DB.lock().unwrap();if db.contains_key(&id) {db.insert(id, item.get("value").unwrap_or(&String::new()).to_string());Ok(warp::reply::json(&item))} else {Err(warp::reject::not_found())}
}// 处理 DELETE 请求,删除指定项目
async fn delete_item(id: String) -> Result<impl warp::Reply, warp::Rejection> {let mut db = DB.lock().unwrap();if db.remove(&id).is_some() {Ok(warp::reply())} else {Err(warp::reject::not_found())}
}

2. 过滤器的工作原理

在Warp框架中,routes变量是通过组合多个过滤器来定义的,这些过滤器共同描述了如何匹配和处理HTTP请求。下面详细解释routes是如何接收参数,并把参数传递给子过滤器的,以及andor运算在Warp过滤器中的作用。

参数传递

Warp的过滤器之间传递参数主要是通过组合和链式调用的方式。当一个请求到达Warp服务器时,它会按照定义的过滤器链进行匹配和处理。在这个过程中,每个过滤器都可以访问和修改请求数据,也可以产生一些中间结果,这些结果可以被链中的后续过滤器使用。

例如,在warp::path!("items" / String)中,String是一个捕获参数,当请求的路径匹配items/<some_string>模式时,<some_string>部分会被捕获并作为一个String类型的值传递给后续的过滤器或处理函数。

and 运算

在Warp中,and运算用于串联过滤器,确保请求必须依次通过所有串联的过滤器。每个and后面的过滤器都会接收到前面过滤器产生的结果,并且可以基于这些结果进行进一步的处理或决策。

例如,在warp::get().and(warp::path!("items" / String))中,warp::get()过滤器首先检查请求方法是否为GET,如果是,则继续将请求传递给warp::path!("items" / String)过滤器进行路径匹配。如果路径也匹配成功,那么捕获到的路径参数会传递给后续的.and_then(get_item)处理函数。

or 运算

or运算在Warp中用于提供备选的路由处理逻辑。当or前面的过滤器链匹配失败时,Warp会尝试匹配or后面的过滤器链。这允许你定义多个不同的路由处理逻辑,并根据请求的具体情况选择合适的逻辑来处理。

在你的代码中,通过连续使用.or运算,你定义了一个能够处理GET、POST、PUT和DELETE请求的路由逻辑。当Warp接收到一个请求时,它会首先尝试匹配GET请求和特定路径的过滤器链,如果匹配失败,则会尝试匹配POST请求的过滤器链,以此类推。

综上所述,routes变量通过组合多个过滤器来定义了一个复杂的路由处理逻辑,其中and运算用于串联过滤器并确保请求必须满足所有条件,而or运算则提供了备选的路由处理逻辑。

3. 关键代码分析

我们开发一下函数 and() 的实现:

    fn and<F>(self, other: F) -> And<Self, F>whereSelf: Sized,<Self::Extract as Tuple>::HList: Combine<<F::Extract as Tuple>::HList>,F: Filter + Clone,F::Error: CombineRejection<Self::Error>,{And {first: self,second: other,}}

继续看 And 这个数据类型的定义:

#[derive(Clone, Copy, Debug)]
pub struct And<T, U> {pub(super) first: T,pub(super) second: U,
}impl<T, U> FilterBase for And<T, U>
whereT: Filter,T::Extract: Send,U: Filter + Clone + Send,<T::Extract as Tuple>::HList: Combine<<U::Extract as Tuple>::HList> + Send,CombinedTuples<T::Extract, U::Extract>: Send,U::Error: CombineRejection<T::Error>,
{type Extract = CombinedTuples<T::Extract, U::Extract>;type Error = <U::Error as CombineRejection<T::Error>>::One;type Future = AndFuture<T, U>;fn filter(&self, _: Internal) -> Self::Future {AndFuture {state: State::First(self.first.filter(Internal), self.second.clone()),}}
}
...

相关文章:

  • SLAM Paper Reading和代码解析
  • Git分支的状态存储——stash命令的详细用法
  • 实时语音翻译软件哪个好?多语言实时沟通无负担
  • BigDecimal 小数位的处理(RoundingMode)
  • Eigen::Isometry3d变换矩阵的常用方法
  • Pwn刷题记录(不停更新)
  • 初中英语优秀作文分析-005How to Plan Our Life Wisely-如何明智地规划我们的生活
  • Java短路逻辑运算符
  • 个人成长的利器:复盘教你如何避免重蹈覆辙
  • windows端口被占用问题,杀死进程
  • Kafka 最佳实践:构建高性能、可靠的数据管道
  • 免费一年SSL证书申请——建议收藏
  • 【机器学习300问】127、怎么使用词嵌入?
  • 如何在 Ubuntu 12.04 VPS 上安装和配置基本的 LDAP 服务器
  • Cesium如何高性能的实现上万条道路的流光穿梭效果
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • CSS魔法堂:Absolute Positioning就这个样
  • ERLANG 网工修炼笔记 ---- UDP
  • PHP变量
  • rc-form之最单纯情况
  • 动态规划入门(以爬楼梯为例)
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 蓝海存储开关机注意事项总结
  • 深度解析利用ES6进行Promise封装总结
  • 原生 js 实现移动端 Touch 滑动反弹
  • 运行时添加log4j2的appender
  • ionic入门之数据绑定显示-1
  • 回归生活:清理微信公众号
  • ​iOS实时查看App运行日志
  • (175)FPGA门控时钟技术
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (floyd+补集) poj 3275
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (vue)el-tabs选中最后一项后更新数据后无法展开
  • (vue)页面文件上传获取:action地址
  • (回溯) LeetCode 46. 全排列
  • (三十五)大数据实战——Superset可视化平台搭建
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Core跨平台微服务学习资源
  • .net framework profiles /.net framework 配置
  • .NET 事件模型教程(二)
  • .Net的C#语言取月份数值对应的MonthName值
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .Net面试题4
  • .NET企业级应用架构设计系列之开场白
  • .NET中使用Redis (二)
  • @GetMapping和@RequestMapping的区别
  • [ Socket学习 ] 第一章:网络基础知识
  • [51nod1610]路径计数
  • [C#] 基于 yield 语句的迭代器逻辑懒执行
  • [C++]四种方式求解最大子序列求和问题
  • [C++数据结构](22)哈希表与unordered_set,unordered_map实现
  • [CISCN2019 华东南赛区]Web11