Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
warp框架教程3-path, method和自定义请求方法
path 是 warp 中的路由系统, 一个 web 框架的灵魂所在, 一个优美的路由系统可以给我们带来非常良好的使用体验, 而 warp 的路由体验本身就是非常 nice 的。在本文中将展示一个 RESTful 风格的 API 设计。下面先来学习一下 path 模块。
zy010101
2023/07/11
5750
warp框架教程3-path, method和自定义请求方法
warp框架教程5-Filter系统中各个模块
any 模块只有一个方法,就是 any 方法,它可以匹配任何路由的过滤器。我们可以使用 any 方法将一些可克隆的资源转换成一个过滤器,从而允许轻松地将它与其他 Filter 结合在一起。当然也可以使用 any 方法创建适用于多个 Filter 的末尾调用的 Reply。例如:
zy010101
2023/07/24
3620
warp框架教程5-Filter系统中各个模块
JAVA学习篇–javaweb之Filter具体解释[通俗易懂]
在DRP项目中,多次提到了Filter,它攻克了字符集的统一设置以及统一控制简单WebCache,从中我们能够体会到。它给我们带来的优点不不过降低代码量这么简单,它的出现避免了我们每一个页面反复的编写同样的代码,降低了我们的工作量。并且给维护带来了极大的便利。那么它是怎样实现统一管理的呢?既然它能统一管理某些反复的操作。那么它和AOP有什么关系呢?
全栈程序员站长
2022/07/10
6270
JAVA学习篇–javaweb之Filter具体解释[通俗易懂]
【译文】Rust组合器
原文:Learning Rust Error Handling Combinators
袁承兴
2021/01/14
6530
【译文】Rust组合器
warp框架教程2-log模块,addr模块和header模块
从本文开始,我们将介绍 warp 中 Filter 的核心模块。在文档中有 filter 相关模块的介绍, 本文来介绍其中的 addr,header 和 log
zy010101
2023/07/11
3940
warp框架教程2-log模块,addr模块和header模块
rust warp框架教程1-helloworld
warp is a super-easy, composable, web server framework for warp speeds.
zy010101
2023/07/11
1.4K0
rust warp框架教程1-helloworld
你应该知晓的Rust Web 框架
在之前的用 Rust 搭建 React Server Components 的 Web 服务器我们利用了Axum构建了RSC的服务器。也算是用Rust在构建Web服务上的小试牛刀。
前端柒八九
2023/11/17
2.9K0
你应该知晓的Rust Web 框架
优雅地组合:谈谈 axum
Axum 是 tokio 官方出品的一个非常优秀的 web 开发框架,一经推出,就博得了我的好感,让我迅速成为它的粉丝。相比之前我使用过的 Rust web 框架,如 rocket,actix-web,axum 对我最大的吸引力就是它优雅的架构:它没有选择从零开始另起炉灶,而是以同样非常优秀的 tower 库的 Service trait 为基石,构建其功能。
tyrchen
2022/03/29
9.3K2
优雅地组合:谈谈 axum
过滤器Filter精华知识点,怎能不看 ​
过滤器Filter 1 什么是过滤器 过滤器JavaWeb三大组件之一,它与Servlet很相似!不它过滤器是用来拦截请求的,而不是处理请求的。 当用户请求某个Servlet时,会先执行部署在这个请求
Java帮帮
2018/03/19
9660
过滤器Filter精华知识点,怎能不看 ​
2023 年值得关注的 6 个 Rust web开发框架
在 2023 年,在开始开发 API 和 Web 应用程序之前,哪个 Rust Web 框架最适合研究?在这里,我们将看看一些目前使用或看起来很有前途的最流行的框架。
程序那些事儿
2023/03/07
10.2K0
2023 年值得关注的 6 个 Rust web开发框架
零成本异步 I/O (下)
这个非常出色的基于轮询的新方案——我们编写了这个模型,我归功于 Alex 和 Aaron Turon,是他们提出了这个想法——不是由 Future 来调度回调函数,而是由我们去轮询 Future,所以还有另一个被称为执行器(executor)的组件,它负责实际运行 Future ;执行器的工作就是轮询 Future ,而 Future 可能返回“尚未准备就绪(Pending)”,也可能被解决就返回“已就绪(Ready)”。
MikeLoveRust
2019/12/17
1K0
零成本异步 I/O (下)
【大家的项目】RiteRaft - Raft 应用快速开发框架,160 行启动一个 Raft 服务
ritedb/riteraft 是一个实用 Raft 框架,用于快速开发和验证基于 Raft 共识算法的分布应用式。
MikeLoveRust
2021/06/16
6070
基于Cookie+Redis+Filter实现Tomcat集群Session共享
用户登录之后,将Session Id和用户信息存储到Redis中,并添加一个Cookie,将该Session Id带到客户端。当发起其他请求之后,携带该Cookie,应用服务器获取到Session Id之后去Redis中查询是否存在,如果存在则继续进行相关业务,否则提示用户未登录。那种在Cookie中存放用户信息的方式直接Pass掉了。
用户1289394
2021/02/05
4890
基于Cookie+Redis+Filter实现Tomcat集群Session共享
SpringMVC开发 知识点速查
SpringMVC入门 什么是SpringMVC 实现MVC设计模式的框架 SpringMVC核心组件 DispatcherServlet 前置控制器,调度 Handler 处理器,完成具体业务逻辑 HandlerMapping 将请求映射到Handler,映射 HandlerInterceptor 处理器拦截器 HandlerExcutionChain 处理器执行链,拦截器 HandlerAdapter 处理器适配器,转换 ModelAndView 装载模型数据和视图信息 ViewResolver 视图解
linxinzhe
2018/07/25
4220
SpringMVC开发 知识点速查
Tera 中文教程:简明易懂的入门指南
Tera 是一个强大的 Rust 模板引擎,灵感来源于 Jinja2 和 Django 的模板系统。它广泛应用于 Web 开发中,用于生成动态 HTML 内容。本文将带领您从零开始,逐步了解如何在 Rust 项目中使用 Tera,包括基本设置、模板语法、高级用法以及内置的过滤器、测试和函数。
訾博ZiBo
2025/01/06
3020
ejs项目源码阅读
ejs项目大名鼎鼎,应该就不需要介绍了,主要收获就是得知了实现一个模板引擎的流程,ejs是将模板作为字符串逐个解析,遇到正常的html代码,就放进一个数组中去,遇到js代码则进行过滤器、包含等的处理,最后数组join成一个可以成为Function构造函数第二个参数的字符串,构造成构造函数之后就是调用返回最终的html字符串。以下是阅读源码的笔记,因为源码中遗憾有很多说明,所以笔记很少。
用户1515472
2019/07/25
2K0
java过滤器Filter「建议收藏」
Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断如是否有权限访问页面等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应 (Request、Response)统一设置编码,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的 web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁,以下通过代码示例来了解它 的使用。
全栈程序员站长
2022/06/27
5440
拦截器(Interceptor)与过滤器(Filter)
  拦截器是面向切面(AOP)编程中应用的一种统一处理方案,就是在你的Controller、Servie或者一个Method调用一个Method,或者在Method调用一个Method之后,统一的进行处理的方案,基于Java的反射机制。
BUG弄潮儿
2021/06/25
4.7K0
拦截器(Interceptor)与过滤器(Filter)
鹅厂火热开发框架:trpc-go设计理念介绍
作者:ronaldoliu,腾讯 IEG 后台开发工程师 trpc-go 是目前公司运用广泛的一个开发框架,支持多协议扩展,能够一键集成各种公司现有平台的功能,非常方便。那么它到底是怎么做到的呢? trpc-go 是目前公司里非常火热的一个开发框架,集成了很多开箱即用的功能,非常方便。trpc-go 代码量不算太多,但是写得还是有点绕,直接阅读可能会比较晕。因此本文主要对 trpc-go 的模块设计进行一个分享,帮助大家构建一个整体视图,后续有需要再针对性的去阅读各模块源码即可。 做后端开发的同学肯定接触过
腾讯技术工程官方号
2023/01/11
4.7K6
鹅厂火热开发框架:trpc-go设计理念介绍
es中的analyzer,tokenizer,filter你真的了解吗?
最近在做搜索推荐相关的需求,有一个场景中需要某一列能处理多种分词器的分词匹配,比如我输入汉字或拼音或语义相近的词都需要把匹配结果返回回来。经过一番调研,最终我们选择了elasticsearch来处理数据的索引与搜索,在配置分词器时会发现大多分词器配置中都需要配置analyzer、tokenizer、filter,那么这三个东西分别代表着什么,又有什么样的联系呢?这就是本文要重点讨论的事情。关于如何在elasticsearch中使用分词器[1]以及常用的中文分词器[2]和拼音分词器[3]是什么,该怎么样去配置这些问题不是本文要讨论的重点,链接都已经奉上,需要的自取。本文咱们就来聚焦讨论一下analyzer、tokenizer、filter之间的区别与联系。
山行AI
2021/07/23
7.6K0
es中的analyzer,tokenizer,filter你真的了解吗?
相关推荐
warp框架教程3-path, method和自定义请求方法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验