[Rust开发]actix_webmiddleware 中间件

actix_web::middleware 在 Actix Web 框架中扮演着重要的角色,它允许开发者在处理 HTTP 请求和响应的过程中插入自定义的逻辑。中间件可以在请求到达处理函数之前或响应返回给客户端之前执行,从而实现日志记录、身份验证、数据验证、错误处理等功能。

为什么要有中间件?

代码在处理逻辑的时候,通常只处理正常的业务逻辑,而在处理过程中,可能会遇到一些特殊的情况,比如:404错误,这种错误会让客户端的请求无法进入到代码功能中,这时候就需要中间件来处理这些特殊的情况。

另外,还可以起到Java里面的过滤器功能,在Java里面,过滤器可以在请求到达处理函数之前或响应返回给客户端之前执行,从而实现对请求和响应的预处理和后处理。

在Rust开发中,特别是在使用Actix Web框架时,中间件(middleware)是一种非常有用的设计模式,它允许开发者在处理HTTP请求和响应的过程中插入自定义的逻辑。以下是使用中间件的几个主要原因:

  • 代码模块化和可重用性:中间件可以将通用的功能(如日志记录、身份验证、数据验证等)封装起来,使得这些功能可以被多个处理函数共享和重用。这样可以减少代码重复,提高代码的可读性和可维护性。

  • 灵活性和可扩展性:通过使用中间件,开发者可以灵活地组合和配置不同的功能,以满足应用程序的特定需求。例如,可以根据不同的路由或用户角色应用不同的中间件。

  • 请求和响应的预处理和后处理:中间件可以在请求到达处理函数之前或响应返回给客户端之前执行,从而实现对请求和响应的预处理和后处理。例如,可以在请求到达之前记录日志、验证用户身份,或者在响应返回之前压缩数据、添加额外的头部信息等。

  • 提高开发效率:使用中间件可以减少开发者编写重复代码的工作量,使得开发者可以更专注于业务逻辑的实现。同时,中间件的模块化设计也使得代码的测试和调试更加容易。

  • 支持异步编程模型:Actix Web是一个基于异步IO的Web框架,中间件可以很好地适应这种编程模型,允许开发者在不阻塞主线程的情况下执行耗时的操作。

以下是一些常见的中间件及其作用:

  • 日志记录:记录每个请求的详细信息,如请求方法、路径、时间戳等,有助于调试和监控。
  • 身份验证:验证用户的身份,确保只有经过授权的用户才能访问特定的资源或执行特定的操作。
  • 数据验证:在请求到达处理函数之前,验证请求数据的格式和内容是否符合预期,防止无效或恶意数据进入系统。
  • 错误处理:统一处理应用程序中的错误,提供友好的错误信息给客户端,同时记录错误日志以便后续分析。
  • 性能监控:测量每个请求的处理时间,帮助开发者识别性能瓶颈并进行优化。
  • 跨域资源共享(CORS):处理跨域请求,允许或拒绝来自不同域的请求,确保安全的跨域数据交互。
  • 使用中间件可以使代码更加模块化和可重用,开发者可以根据需要组合和配置不同的中间件,以满足应用程序的特定需求。在 Actix Web 中,中间件通常通过 App::wrap 方法进行注册和应用。

自定义中间件的过程:

利用闭包实现简单的中间件

例如我要定义一个预先获取请求中header里面的token,如果没有这个token则直接就返回错误。代码可以这样写:

use actix_web::dev::Service;
use actix_web::error;
use actix_web::{middleware::{self, Logger}, web, App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {HttpServer::new(move || {App::new().wrap(middleware::Logger::default()).wrap_fn(|req, srv| {let header = req.headers().to_owned();let fut = srv.call(req);async move {let res = fut.await;match header.get("token"){Some(token) => {println!("token:{}", token.to_str().unwrap());},None => {println!("token is None");return Err(error::ErrorUnauthorized("Unauthorized"));},}res}}).route("/", web::get().to(|| async { "Hello, World!" }))}).bind("127.0.0.1:8080")?.run().await
}

这里的核心,是使用wrap_fn + 一个闭包来实现中间件。

在示例代码中,wrap_fn 被用来创建一个中间件,这个中间件检查请求头中是否包含 token。如果没有 token,它会返回一个未经授权的错误。如果有 token,它会调用下一个服务并返回其结果。

warp_fn的源码如下(看不懂也没关系,不用懂,我写这里是为了你以后能看懂时候回来查资料用的)

pub fn wrap_fn<F, Fut>(f: F) -> impl Transform<ServiceRequest, Response = ServiceResponse<Fut::Item>, Error = Fut::Error, InitError = ()>
whereF: Fn(ServiceRequest, &mut dyn Service<ServiceRequest, Response = ServiceResponse<Fut::Item>, Error = Fut::Error, Future = Fut>) -> Fut,Fut: Future<Output = Result<ServiceResponse<Fut::Item>, Fut::Error>> + 'static,

里面参数解释如下:

  • 输入参数:

    • F: 这是一个函数类型,它接受两个参数:ServiceRequest 和一个可变引用 &mut dyn Service<…>。这个函数将返回一个 Fut 类型的 Future。
    • Fut: 这是一个 Future 类型,它的输出是 Result<ServiceResponseFut::Item, Fut::Error>。Fut::Item 是响应体的类型,Fut::Error 是错误类型。
  • 返回值:

    • impl Transform<ServiceRequest, Response = ServiceResponseFut::Item, Error = Fut::Error, InitError = ()>: 这是一个实现了 Transform trait 的类型,它可以转换 ServiceRequest 到 ServiceResponseFut::Item,并且可以处理 Fut::Error 类型的错误。

执行结果如下:

如果是一些建议的逻辑,我们用wrap_fn + 闭包就可以了,但是如果是一些复杂的逻辑,就需要自己实现Transform trait了。

重新实现Transform trait 来实现自定义的中间件
  1. 中间件初始化:在这个阶段,中间件工厂函数被调用,它接收链中的下一个服务作为参数。这允许中间件在实际处理请求之前进行任何必要的设置或配置。
  • 中间件工厂类是Transform``trait的。

    • S - 后续服务的类型:S 代表链中下一个服务的类型,即当前中间件将请求传递给哪个服务。
    • B - 响应体的类型:B 代表响应体(response)的类型,指定了从服务返回的响应内容的格式。

    //中间的工厂类
    pub struct Auth;

    impl<S, B> Transform<S, ServiceRequest> for Auth
    where
    S: Service<ServiceRequest, Response = ServiceResponse, Error = Error>,
    S::Future: 'static,
    B: 'static,
    {
    type Response = ServiceResponse;
    type Error = Error;
    type InitError = ();
    type Transform = AuthMiddleware;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {ready(Ok(AuthMiddleware { service }))
    }
    

    }

  1. 编写中间件的具体实现,核心是中间件的 call 方法调用:一旦中间件初始化完成,每当有新的请求到来时,中间件的 call 方法就会被调用,并接收这个普通请求作为参数。此时,中间件可以对请求进行处理

    // 中间件的具体实现,里面需要接受工厂类里面过来的service
    pub struct AuthMiddleware {
    service: S,
    }

    //具体实现
    //核心是两个方法:
    // call 具体实现
    // poll_ready
    impl<S, B> Service for AuthMiddleware
    where
    S: Service<ServiceRequest, Response = ServiceResponse, Error = Error>,
    S::Future: 'static,
    B: 'static,
    {
    type Response = ServiceResponse;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    // 实现 poll_ready 方法,用于检查服务是否准备好处理请求 
    //这里用的是forward_ready!宏
    forward_ready!(service);// 实现 call 方法,用于处理实际的请求
    fn call(&self, req: ServiceRequest) -> Self::Future {// 进行鉴权操作,判断是否有权限if has_permission(&req) {// 有权限,继续执行后续中间件let fut = self.service.call(req);Box::pin(async move {let res = fut.await?;Ok(res)})} else {// 没有权限,立即返回响应Box::pin(async move {// 鉴权失败,返回未授权的响应,停止后续中间件的调用Err(error::ErrorUnauthorized("Unauthorized"))})}
    }
    

    }

    fn has_permission(req: &ServiceRequest) -> bool {
    // 实现你的鉴权逻辑,根据需求判断是否有权限
    // 返回 true 表示有权限,返回 false 表示没有权限
    // unimplemented!()
    let value = HeaderValue::from_str(“”).unwrap();
    match req.path().to_ascii_lowercase().as_str(){
    “/login” => true,
    _ => {
    let token = req.headers().get(“token”).unwrap_or(&value);
    if token.len() <=0{
    false
    }else{
    println!(“验证一下token,看看是否合法”);
    true
    }
    }
    }
    }

核心方法说明:
  • poll_ready: 这个方法用于检查服务是否准备好处理请求。 它返回一个Poll类型的结果,表示服务是否就绪。 如果服务已经就绪,返回Poll::Ready(Ok(()))。 如果服务尚未就绪,返回Poll::Pending,表示需要等待一段时间后再次检查。 在middleware中,poll_ready方法通常用于确保在处理请求之前,所有依赖的资源或服务都已经准备就绪。

  • call: 这个方法用于处理实际的请求。 它接收一个ServiceRequest类型的参数,并返回一个ServiceResponse类型的结果。 在middleware中,call方法通常用于对请求进行预处理或后处理,例如添加日志记录、验证请求、修改响应等。 call方法的返回值是一个Future,表示异步处理的结果。

所以也可以自己实现poll_ready

fn poll_ready(&self, ctx: &mut core::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {if self.service.poll_ready(ctx).is_pending() {// 如果服务尚未准备好,返回Pendingreturn std::task::Poll::Pending;}// 如果服务已准备好,返回Ready(Ok(()))std::task::Poll::Ready(Ok(()))
}

使用中间件

#[actix_web::main]
async fn main() -> std::io::Result<()> {//中间件的顺序是从下到上的,最后注册的中间件会最先执行HttpServer::new(move || {App::new().wrap(middleware::Logger::default()).wrap(Auth::Auth)// 注册其他路由和处理函数.route("/", web::get().to(|| async { "Hello, World!" })).route("/login", web::get().to(|| async { "Hello, login" }))}).bind("127.0.0.1:8080")?.run().await
}

执行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/64936.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WebLogic T3反序列化漏洞(CVE-2018-2628)--vulhub

WebLogic T3反序列化漏洞(CVE-2018-2628) WebLogic在通信过程中使用T3协议传输数据&#xff0c;涉及到了序列化和反序列化操作。 T3协议概述 T3协议是Oracle的私有协议&#xff0c;所以公开的相关资料比较少&#xff0c;这里结合其他师傅的博客简单对T3协议进行一个简要分析…

OpenFeign快速入门 示例:黑马商城

使用起因 之前我们利用了Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。这样一来购物车虽然通过远程调用实现了调用商品服务的方法,但是远程调用的代码太复杂了: 解决方法 并且这种调用方式比较复杂&#xff0c;一会儿远程调用&#xff0c;一会儿本地调用。 因…

计算机的错误计算(一百九十)

摘要 用两个大模型计算cot(1.234). 其中&#xff0c;1.234是以弧度为单位的角度。结果保留10位有效数字。实验表明&#xff0c;两个的计算公式虽然不同&#xff0c;但是都是正确的。然而&#xff0c;数值计算则是有问题的---包括每一个中间运算与结果。 例1. 计算cot(1.234)…

【QSS样式表 - ⑥】:QPushButton控件样式

文章目录 QPushBUtton控件样式QSS示例 QPushBUtton控件样式 常用子控件 常用伪状态 QSS示例 代码&#xff1a; QPushButton {background-color: #99B5D1;color: white;font-weigth: bold;border-radius: 20px; }QPushButton:hover {background-color: red; }QPushButton:p…

深度学习与图像处理(国产深度学习框架——飞桨官方指定教材)

计算机视觉从小白到大师之路 《深度学习与图像处理&#xff08;PaddlePaddle版&#xff09;》这一本就够了 1.引言 随着人工智能技术的飞速发展&#xff0c;各行各业对深度学习、图像处理相关领域的人才需求日益迫切。本书旨在通过系统的理论讲解与丰富的实战案例&#xff0…

HCIA-Access V2.5_6_3_GPON关键技术

GPON关键技术 GPON关键技术包含三个&#xff0c;测距&#xff0c;DBA&#xff0c;下行AES加密。 为什么需要测距 由于每一个ONU到OLT的距离是不一样的&#xff0c;虽然上行有TDMA技术&#xff0c;让每一个ONU在不同的时间段发送数据&#xff0c;但是可能由于距离的原因&#x…

v3s点RGB屏 40pin 800x480,不一样的点屏,不通过chosen。

一、背景、目的、简介。 一般来说&#xff0c;通过uboot将屏幕参数传给kernel&#xff0c;是通过修改设备树。 uboot和kernel都需要屏幕点亮。uboot侧重于显示一张图片。而kernel则多是动画。 在这里&#xff0c;我先是找到了一个裸机点屏的代码。将其编译成静态库后&#x…

电脑丢失bcrypt.dll文件是什么原因?找不到bcrypt.dll文件修复办法来啦!

电脑运行时常见问题及解决方案&#xff1a;文件丢失、文件损坏与系统报错 作为一名软件开发从业者&#xff0c;深知电脑在日常使用中难免会遇到各种问题&#xff0c;如文件丢失、文件损坏和系统报错等。这些问题不仅影响工作效率&#xff0c;还可能带来数据丢失的风险。今天&a…

【自动驾驶】3 激光雷达③

5 激光雷达点云检测模型 &#x1f98b;&#x1f98b;&#x1f98b;CenterPoint是Anchor‐Free的3D物体检测器&#xff0c;以点云作为输入&#xff0c;将三维物体在Bird‐View下的中心点作为关键点&#xff0c;基于关键点检测的方式回归物体的尺寸、方向和速度。相比于Anchor‐…

自动化测试框架playwright 常见问题和解决方案

自动化课程已经讲完了playwright框架&#xff0c;很多同学跃跃欲试&#xff0c;所谓实践出真知&#xff0c;这不在实践中就要到了一些问题&#xff0c;小编也给大家整理出来了&#xff0c;送个有需要的同学&#xff0c;记得点赞收藏哦~~ 01安装问题 问题描述&#xff1a; 在安…

Windows10 下通过 Visual Studio2022 编译 openssl 3.4 + POCO 1.14.1

Windows10 下通过 Visual Studio2022 编译 POCO库 1 POCO库简介2 环境准备2.1 VS Studio 2022 安装2.2 openssl 安装3 编译 POCO 1.14.13.1 下载源码3.2 修改编译配置3.2.1 修改 poco\Crypto 工程 引用 openssl 的配置3.2.2 修改 poco\NetSSL_OpenSSL 工程 引用 openssl 的配置…

厉害了多模态对齐!新思路直接发高区!小红书、国科大都在抢着发!

多模态是个非常热门的话题&#xff0c;这其中&#xff0c;“多模态对齐”已经被验证非常重要&#xff0c;它能够提升AI模型的跨模态理解和情感分析精度&#xff0c;是未来多模态大模型商业化的必要条件&#xff0c;研究热度不言而喻。 就说最近的大佬团队&#xff0c;小红书前…

ubuntu20.04安装imwheel实现鼠标滚轮调速

ubuntu20.04安装imwheel实现鼠标滚轮调速 Ubuntu 系统自带的设置中仅具备调节鼠标速度的功能&#xff0c;而无调节鼠标滚轮速度的功能。其默认的鼠标滚轮速度较为缓慢&#xff0c;在查看文档时影响尚可接受&#xff0c;但在快速浏览网页时&#xff0c;滚轮速度过慢会给用户带来…

MacOS安装MySQL

官网下载MySQL 苹果芯片选择ARM版本 安装过程中会要求你输入root的密码&#xff08;不少于8位&#xff09;&#xff0c;这里设置为12345678 打开系统设置查看是否成功安装MySQL 配置MySQL环境变量 vi ~/.zshrc加入一行export PATH$PATH:/usr/local/mysql/bin 执行source ~/…

dolphinscheduler服务注册中心源码解析(三)RPC提供者服务整合注册中心注册服务实现源码

RPC提供者服务整合注册中心注册服务实现源码 1.概述2.源码解读思路3.实现2.1.应用服务的RPC服务接口定义2.1.1.MasterServer应用中提供的RPC接口服务2.1.2.WorkerServer应用中提供的RPC接口服务2.2.应用服务的RPC服务接口实现2.2.1.MasterServer应用中提供的RPC接口服务实现类2…

Leetcode Hot 100 【二叉树】104. 二叉树的最大深度

104. 二叉树的最大深度 已解答 简单 相关标签 相关企业 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3…

WebDAV文件共享:将个人电脑变身为云存储,实现自由文件传输

WebDAV文件共享&#xff1a;将个人电脑变身为云存储&#xff0c;实现自由文件传输 引言&#xff1a;操作步骤搭建安装Internet Information Services (IIS) 管理器配置Internet Information Services (IIS) 管理器配置远程域名访问地址 引言&#xff1a; 相信很多朋友都有过把…

MySQL的分析查询语句

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

前端开发 之 12个鼠标交互特效上【附完整源码】

前端开发 之 12个鼠标交互特效上【附完整源码】 文章目录 前端开发 之 12个鼠标交互特效上【附完整源码】一&#xff1a;彩色空心爱心滑动特效1.效果展示2.HTML完整代码 二&#xff1a;彩色实心爱心滑动特效1.效果展示2.HTML完整代码 三&#xff1a;粒子连结特效1.效果展示2.HT…

深度学习之超分辨率算法——SRGAN

更新版本 实现了生成对抗网络在超分辨率上的使用 更新了损失函数&#xff0c;增加先验函数 SRresnet实现 import torch import torchvision from torch import nnclass ConvBlock(nn.Module):def __init__(self, kernel_size3, stride1, n_inchannels64):super(ConvBlock…