前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >warp框架教程4-Filter系统中的方法介绍

warp框架教程4-Filter系统中的方法介绍

作者头像
zy010101
发布于 2023-07-24 08:41:15
发布于 2023-07-24 08:41:15
45400
代码可运行
举报
文章被收录于专栏:程序员程序员
运行总次数:0
代码可运行

Filter系统中的方法介绍

过滤器可以选择性地从 request 中提取一些数据,将其与其他数据组合、修改,并将某个值作为 response 返回。过滤器的强大之处在于能够将其拆分为小的子集,然后在应用程序的各个部分中进行链式调用和重用。

正如我们在前文见到的自定义请求方法一样。filter 是从元组中提取值的。

如果一个 filter 提取了一个元组(String,),那就意味着它提取了一个String类型。如果你对该过滤器的结果进行使用(map或者and_then中的func得到的参数就是过滤器返回的值),那么func的参数类型将会确切地是String类型,而不是元组。

这只是一些类型的魔法,它可以自动组合和展平元组。没有这个功能,将两个过滤器用 and 连接在一起,其中一个提取了(),另一个提取了String类型,那么map或者and_then中的func得到的参数类型将会是((),String,),这样就不太方便了。warp会在我们调用map或者and_then的时候,自动解包元组。

filter 提供的方法

and 方法

and方法用来增加一个新的filter,该过滤器要求同时使用当前过滤器和另一个过滤器来过滤请求。

此外,它还会将两个过滤器提取的值合并在一起,以便让 map 和 and_then 作为单独的参数接收到这些值。

如果一个过滤器没有提取任何内容(即()类型),与任何其他过滤器的组合将简单地丢弃()类型。如果一个过滤器提取了一个或多个项目,组合操作将意味着它提取了自身的值与另一个过滤器的值的组合。借用上篇文章的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let edit_user = user_router
    .and(warp::path::param())
    .and(warp::path::end())
    .and(warp::put())
    .and_then(edit_user);       

and组合了warp::path::param(), warp::path::end() 以及 warp::put() 三个filter,这三个方法的声明如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pub fn param<T>() -> impl Filter<Extract = One<T>, Error = Rejection> + Copy
where
    T: FromStr + Send + 'static,

pub fn end() -> impl Filter<Extract = (), Error = Rejection> + Copy

pub fn put() -> impl Filter<Extract = (), Error = Rejection> + Copy

那么and将会组合这三个过滤器的结果。由于 end 和 put 的 Extract 都是(), 这意味着在和 param 组合的时候 () 将会被丢弃,因此最终组合的结果将是 param 的 Extract 的值。这个值将会被传入到 edit_user 函数的第一个参数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async fn edit_user(id: u32) -> Result<impl warp::Reply, warp::Rejection> {
    Ok(format!("修改用户{}信息", id))
}

如果有多个过滤器,并且 Extract 返回多个参数。那么传入到 map 或者 and_then 中的参数顺序是按照以 and 添加 filter 的顺序来组合的。

or 方法

和 and 方法类似,只不过 or 方法要求要么使用当前过滤器,要么使用另一个过滤器。or 将根据条件选择性地使用其中一个过滤器来处理请求。正如我们前一篇文章中最后组合的 apis。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let apis = hello
    .or(create_user)
    .or(login_user)
    .or(logout_user)
    .or(edit_user)
    .or(delete_user);

or_else 方法

使用 or_else 方法将当前过滤器与一个接收错误的函数组合。

该函数应返回一个产生与当前过滤器相同的项目类型和错误类型的 TryFuture。

then 方法

使用 then 方法将当前过滤器与一个接收提取值的异步函数组合,该函数应返回一个产生某种类型的 Future。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use warp::Filter;

// Map `/:id`
warp::path::param().then(|id: u64| async move {
  format!("Hello #{}", id)
});

map 方法

map 方法将会接收 filter 的结果。需要特别注意的是 如果有多个过滤器,并且 Extract 返回多个参数。那么传入到 map 或者 and_then 中的参数顺序是按照以 and 添加 filter 的顺序来组合的。例如我们的第一个 warp 程序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use warp::Filter;

let hi = warp::path("hello")
    .and(warp::path::param())
    .and(warp::header("user-agent"))
    .map(|param: String, agent: String| {
        format!("Hello {}, whose agent is {}", param, agent)
    });

这里 map 中的闭包接收了两个参数,分别是 param 方法和 header 方法的结果。它们按照 and 的顺序被传递给闭包。还有一点是,map 接受的闭包是同步的,而 and_then 接受的闭包是异步的

and_then 方法

and_then 方法将当前过滤器与一个可能出错的异步函数组合,该函数接收提取的值。和 map 不同的是,and_then 接受的闭包应返回一个产生某种类型的 TryFuture。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async fn create_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("创建用户".to_string())
}

async fn login_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("用户登录".to_string())
}

async fn logout_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("用户退出".to_string())
}

另外一点是,在 warp 文档中建议我们,如果是应用程序级别的错误,那么我们更应该使用 then,而不是 and_then。个人理解是如果使用restful风格,就用and_then;如果是采用自定义状态码来定义错误,那么就使用then

untuple_one 方法

类似于 map 的操作并不返回新的值,当返回值是 () 的时候,这个方法非常有用,因为 warp 会将其包装成 ((),) 而使用 untuple_one 方法可以移除一个元组层级。例如上篇文章中提到的自定义请求方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#[derive(Debug)]
struct MethodError;
impl warp::reject::Reject for MethodError {}

fn method(name: &'static str) -> impl Filter<Extract = (), Error = warp::Rejection> + Clone {
    warp::method()
        .and_then(move |m: Method| async move {
            if m == name {
                Ok(())
            } else {
                Err(warp::reject::custom(MethodError))
            }
        })
        .untuple_one()
}

在最后调用 untuple_one 方法,可以移除一个层级的元组,从而不用将Extract 声明为 ((),)

recover 方法

recover 方法将当前的过滤器与一个接收错误并返回新类型而不是相同类型的函数组合。

你可以根据自己的需求定义一个处理错误并返回自定义响应类型的函数,然后使用 recover 方法将该函数与过滤器组合起来。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Controller
let delete_user = user_router
    .and(warp::path::param())
    .and(warp::path::end())
    .and(warp::delete())
    .and_then(delete_user)
    .recover(err_handler);    

// Error Handler
async fn err_handler(err: Rejection) -> Result<impl Reply, Rejection> {
    let code; 
    if err.is_not_found() {
        code = StatusCode::NOT_FOUND;
    } else {
        code = StatusCode::BAD_REQUEST;   
    }

    let mut res = Response::default();
    res.status_mut().clone_from(&code);
    Ok(res)
}

现在,我们使用 curl 访问一下我们的服务。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
curl -X DELETE -w "%{http_code}\n" 127.0.0.1:3030/user

由于我们没有传递路径参数,因此 warp::path::param() 方法将会抛出一个错误,这个错误将会被传递到 recover 的参数 err_handler 的参数 err 中,根据我们的 err_handler 中的逻辑处理,由于缺少路径参数,warp::path::param() 抛出的错误是 not found,因此我们返回的状态码是 404,如果是其他参数那就是 400.

如果我们没有用 recover 做自定义错误处理,那么 warp::path::param() 返回一个错误,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Unhandled rejection: MethodError500

并且此时的响应状态码是 500.

boxed 方法

boxed 方法用于将一个过滤器(Filter)转换为一个 trait 对象(trait object),使得更容易使用类型的名称。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use warp::Filter;

fn impl_reply() -> warp::filters::BoxedFilter<(impl warp::Reply,)> {
    warp::any()
        .map(warp::reply)
        .boxed()
}

fn named_i32() -> warp::filters::BoxedFilter<(i32,)> {
    warp::path::param::<i32>()
        .boxed()
}

fn named_and() -> warp::filters::BoxedFilter<(i32, String)> {
    warp::path::param::<i32>()
        .and(warp::header::<String>("host"))
        .boxed()
}

with 方法

with 方法用于包装当前过滤器(Filter)以添加一些包装器(Wrapper)。

这个方法允许在当前过滤器之前和之后执行一些准备工作和后处理工作。包装器可以是一个闭包、函数或自定义的结构体,用于在过滤器运行前后执行额外的逻辑。例如第二篇文章中提到的 log 就是放在 with 方法中的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use warp::Filter;

fn main() {
    // 定义一个过滤器
    let filter = warp::path("hello")
        .map(|| "Hello, world!")
        .with(warp::log("request"));

    // 启动 Warp 服务器并使用过滤器
    warp::serve(filter).run(([127, 0, 0, 1], 3030));
}

with 方法的作用看起来非常像是中间件,但是实际上我们实现的任何 filter 都是某种意义上的中间件。例如身份认证,不仅可以放在with 方法中,也可以放在 and 方法中。

inify 方法

unify 方法用于统一合并通过 Filter::or 组合的两个过滤器提取的相同类型的值。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use std::net::SocketAddr;
use warp::Filter;

let client_ip = warp::header("x-real-ip")
    .or(warp::header("x-forwarded-for"))
    .unify()
    .map(|ip: SocketAddr| {
        // Get the IP from either header,
        // and unify into the inner type.
    });

这个例子中,展示了 web 应用程序在有反向代理的情况下,获取客户端真实 IP 的方式,通常是获取 x-real-ip 或者 x-forwarded-for 中第一个 IP,因此可以使用 unify 将两个 HTTP header 组合后提取了相同类型,然后传递给 map 中的闭包进行统一处理。

参考资料

warp文档:https://docs.rs/warp/0.3.5/warp/index.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Qt Quick中的信号与槽
  在QML中,在Qt Quick中,要想妥善地处理各种事件,肯定离不开信号与槽,本博的主要内容就是整理Qt 中的信号与槽的内容。
全栈程序员站长
2022/07/18
1.1K0
Qt开发-QT Quick
Row 则是一个单独的 Item ,专门用来管理其它 Item 的,后面介绍的几种布局,也是类似的。
码客说
2021/11/10
2.6K0
QML动态显示组件(支持在线编辑动态刷新)
QML动态组件显示器主要用于方便界面开发,在线编辑保存后自动刷新组件界面,并支持拖拽文件显示的方式。
Qt君
2019/07/15
5.5K0
Qt官方示例-QML标签页
❝TabWidget示例演示了如何使用属性别名和QML Object默认属性创建标签页。❞ TabWidget.qml Item { id: tabWidget // 核心实现 // 将默认属性设置为stack.children意味着TabWidget的所有子项实际上都已添加到"stack"项的子项中。 // 有关默认属性的详细信息,请参见"Property Binding"文档。 default property alias content: stack.c
Qt君
2023/03/17
1.3K0
Qt官方示例-QML标签页
qt5中信号和槽的新语法
qt5中的连接 有下列几种方式可以连接到信号上 旧语法 qt5将继续支持旧的语法去连接,在QObject对象上定义信号和槽函数,及任何继承QObjec的对象(包含QWidget)。 connect(sender, SIGNAL (valueChanged(QString,QString)),receiver, SLOT (updateValue(QString)) ); 新语法:连接到QObject成员 下面是一种新的方式来连接两个QObjects: connect(sender, &Sender::val
蘑菇先生
2018/05/21
1.9K0
ant design中upload组件 上传图片压缩
不想描述多余的,直接看代码简单直接 const [defaultFileList, setDefaultFileList] = useState([]); <Upload accept="image/*" customRequest={uploadImage} onChange={handleOnChange} listType="picture-card"
我乃小神神
2021/12/07
1.5K0
拥抱 Vue 3 系列之 JSX 语法
“别再更新了,学不动了”。这句话不知道出了多少开发者的辛酸。在过去的一年中,Vue 团队一直都在开发 Vue.js 的下一个主要版本,就在 6 月底,尤大更新同步了 Vue 3 及其周边生态的状态:Vue 3: mid 2020 status update (https://github.com/vuejs/rfcs/issues/183)。
政采云前端团队
2020/07/24
2.3K0
【Qt源码笔记】Qt事件与Windows消息循环的联系
上次研究了一下Qt是如何对Win32初始化程序进行包装的。这次研究下Qt的事件循环和Windows消息循环之间的联系。
Harper
2021/07/27
2.5K0
Hello Qt——QMake用户指南[通俗易懂]
QMake扩展了每个工程文件的信息,生成一个执行编译和链接过程的必须命令的MakeFile。
全栈程序员站长
2022/08/31
6.5K0
linux下编译qt5.6.0静态库
编译QT是一件比较麻烦的事情。所以如果没有必要,就不要编译了。如果你只需要使用QT的一些基本功能,那么就只编译源码目录下的qtbase目录下的东西即可。 下面所介绍的都只是在linux下适用的(windows下使用MinGW也可以,QT提供的MinGW安装包是32位的,需要64位的可以参考)。 在编译前,最好先安装完成相关的依赖包,主要是xcb/opengl等。编译器使用gcc5及以上版本速度会比较快。i5-4460 CPU @ 3.20GHz+8G内存下编译时间大概是2.5个小时。 编译的时候对磁盘空间要求非常非常的大75G ./qt-src/qt-opensource-src-5.6.0(这是还没有完全编译完时候的情况),如果无法接受,就别编译debug版本和examples了。
用户3519280
2023/07/06
1.2K0
linux下编译qt5.6.0静态库
vue项目使用 富文本 封装
我又来了,今天给大家分享一个富文本框的封装,写后台管理也离不开富文本框,我就做了封装,供大家参考,
前端小白@阿强
2020/08/12
1.3K0
HTML5 学习总结(三)——本地存储(localStorage、sessionStorage、WebSqlDataBase、IndexedDB)
HTML5问世以后,前端加入了一个重要的功能,便是本地存储,本地存储可分为4类: Local Storage:总的存储量有所限制,并不能提供真正的检索API,数据的生命期比窗口或浏览器的生命期长,数据
张果
2018/01/03
7.7K0
HTML5 学习总结(三)——本地存储(localStorage、sessionStorage、WebSqlDataBase、IndexedDB)
论egret的坑
这样是会被认为不是用户手动触发的,是会被浏览器默认拦截的,不要写在call里边,但是可以写在settimeout里边
陨石坠灭
2020/01/21
1.6K0
论egret的坑
v-model数据绑定分析
v-model是Vue提供的指令,其主要作用是可以实现在表单<input>、<textarea>及<select>等元素以及组件上创建双向数据绑定,其本质上就是一种语法糖,既可以直接定义在原生表单元素,也可以支持自定义组件。在组件的实现中,可以配置子组件接收的prop名称,以及派发的事件名称实现组件内的v-model双向绑定。
WindRunnerMax
2020/10/26
1.9K0
vue源码分析-v-model的本质
由于v-model和前面介绍的插槽,事件一致,都属于vue提供的指令,所以我们对v-model的分析方式和以往大同小异。分析会围绕模板的编译,render函数的生成,到最后真实节点的挂载顺序执行。最终我们依然会得到一个结论,v-model无论什么使用场景,本质上都是一个语法糖。
yyzzabc123
2022/10/18
1K0
qmake手册(Qt5.9.3)
qmake工具有助于简化跨平台项目的开发构建过程。qmake自动生成Makefiles,这样只需要几行信息就可以创建Makefile。您可以在任何软件项目中使用qmake,无论是否使用Qt编写。
全栈程序员站长
2022/08/31
5.9K0
【QT】:QT(介绍、下载安装、认识 QT Creator)
所谓客户端就是直接和用户打交道的一端从程序,就比如 chrome,cctalk,……
IsLand1314
2024/12/20
4.2K0
【QT】:QT(介绍、下载安装、认识 QT Creator)
阿里前端面试问到的vue问题
虽然文档不建议在应用中直接使用 mixin,但是如果不滥用的话也是很有帮助的,比如可以全局混入封装好的 ajax 或者一些工具函数等等。
bb_xiaxia1998
2022/10/29
9250
websocket+rabbitmq实战
  接到的需求是后台定向给指定web登录用户推送消息,且可能同一账号会登录多个客户端都要接收到消息
老梁
2019/09/10
2.7K1
Qt学习之路_6(Qt局域网聊天软件)
http://www.cnblogs.com/tornadomeet/archive/2012/07/04/2576355.html
bear_fish
2018/09/20
3.4K0
Qt学习之路_6(Qt局域网聊天软件)
相关推荐
Qt Quick中的信号与槽
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验