学习Node js:raw-body模块源码解析

raw-body是什么

raw-body的主要功能是处理HTTP请求体的原始数据。它提供了以下核心功能:

  1. 解析请求体:可以从HTTP请求中提取原始数据,包括文本和二进制数据。
  2. 配置选项:通过配置项,可以设置请求体的大小限制、编码方式等参数。
  3. 异常处理:模块能够处理异常情况,如请求体超出限制。
  4. 编码转换:支持将原始数据解码为指定编码的字符串,或者返回Buffer实例。

express中的body-parser中间件就使用了raw-body来处理请求

raw-body基础用法

安装:

npm install raw-body

引入:

var getRawBody = require('raw-body')

getRawBody函数签名如下:

getRawBody(stream, [options], [callback])

stream是需要解析的流。

options是一些配置项。

  • length - 流的长度。
  • limit - 请求体的大小限制。比如 1000'500kb''3mb'
  • encoding - 用于将请求体解码为字符串的编码。默认情况下,如果未指定编码,将返回 Buffer 实例。最有可能的是,您需要 utf-8 ,因此将 encoding 设置为 true 将解码为 utf-8

callback是解析完成之后的回调函数。

结合express一起使用的例子如下:


var contentType = require('content-type')
var express = require('express')
var getRawBody = require('raw-body')var app = express()app.use(function (req, res, next) {getRawBody(req, {length: req.headers['content-length'],limit: '1mb',encoding: contentType.parse(req).parameters.charset}, function (err, string) {if (err) return next(err)req.text = stringnext()})
})// 可以在后续的express中间件中访问 req.text

也可以使用promise风格调用getRawBody

var getRawBody = require('raw-body')
var http = require('http')var server = http.createServer(function (req, res) {getRawBody(req).then(function (buf) {res.statusCode = 200res.end(buf.length + ' bytes submitted')}).catch(function (err) {res.statusCode = 500res.end(err.message)})
})server.listen(3000)

raw-body v0.0.3源码阅读

我们选择的版本是v0.0.3,选择这个版本的原因非常简单:代码量非常少,只有70行

https://raw.githubusercontent.com/AC-greener/blog-image/main/Untitled.png

  1. 主要作用是处理一些异常,当请求体内容超过限制时会调用stream.resume这个方法销毁这个stream,防止请求数据被缓冲。
  2. 监听stream的一些事件,然后使用回调进行处理,stream之所以能调用ononceremoveListener等方法,是因为stream继承了Nodejs中的EventEmitter模块。
    1. data事件:每当可读流接收到新的数据块时,就会触发data事件。一般用于逐块处理请求体数据。
    2. end事件:end事件在可读流读取完数据后触发,表示数据流结束。
    3. error事件:当可读流发生错误时触发error事件。
    4. close事件:close事件在可读流关闭时触发,表示流已经被关闭,用于在流关闭时进行一些资源清理或收尾工作

onData

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915223514.png

onData的核心代码只有这两行:

吧收到的chunk 放到buffers数组里面,chunk的数据类型默认是Buffer类型

然后使用 chunk.length 返回当前chunk的字节数,并累加起来

onEnd

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915223614.png

onEnd的核心是57行:调用传入的回调函数,并吧Buffer.concat的结果传入

Buffer.concat方法会吧 buffers 中的所有 Buffer 实例连接在一起,返回一个新的 Buffer

cleanup

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915223806.png

cleanup中的主要逻辑就是调用removeListener对请求数据流的事件监听器进行清理,可以防止内存泄漏以及不必要的资源占用。

v0.0.3版本的代码看完之后我们再看看看v2.5.2的

raw-body v2.5.2源码阅读

2.5.2版本的代码有300多行,是0.0.3版本的四倍,不过核心功能是差不多的,差异点如下:

  • options配置项新增了encoding参数:用于吧body解码成指定编码的字符串,默认情况下,如果没有指定编码,将返回一个 Buffer 实例,
  • stream增加了aborted事件的处理:stream.on('aborted', onAborted)

raw-body依赖的模块

2.5.2版本依赖了5个npm模块

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915223839.png

先看一下这些模块的功能:

  • bytes 是一个用于在不同单位之间进行字节转换的Nodejs模块。常用方法如下:
bytes.parse('1KB');// output: 1024
bytes.format(1024);// output: '1KB'
  • http-errors 用于创建HTTP错误对象。它简化了处理HTTP请求时生成错误响应的过程。也可以和ExpressKoaConnect一起使用。用法如下:
var createError = require('http-errors')
var express = require('express')
var app = express()app.use(function (req, res, next) {if (!req.user) return next(createError(401, 'Please login to view this page.'))next()
})
  • iconv-lite 用于处理字符编码的转换。可以在不同的字符编码之间进行转换。
const iconv = require('iconv-lite');
const originalText = '你好,世界!';// 将文本编码为 Buffer
const encodedBuffer = iconv.encode(originalText, 'utf-8'); // 将编码后的 Buffer 解码为文本
const decodedText = iconv.decode(encodedBuffer, 'utf-8');
  • unpipe 用于取消可读流(Readable Stream)和可写流(Writable Stream)之间的数据传输。例如从文件读取流到HTTP响应流。unpipe 库允许你取消这种数据传输。

getRawBody是入口函数,我们一起看一下:

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915223911.png

  1. 针对参数做一些验证以及错误处理
  2. 调用bytes模块的parse方法解析传入的limit参数
  3. 调用readStream函数处理stream,这里做了判断,如果传入了回调函数,则使用回调的方式传递解析之后stream,否则使用promise风格来处理

readStream函数和v0.0.3版本的代码变化不太大:

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915224042.png

  1. 调用getDecoder函数,用于获取指定编码的解码器。而getDecoder函数里面又调用了iconv模块的getDecoder方法
  2. 监听streamaborted事件,当客户端中止 HTTP 请求时,可读流会触发 aborted 事件。比如在请求尚未完成时客户端提前关闭了连接。
    onAborted函数如下:

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915224127.png

核心逻辑就是调用createError创建一个错误信息,然后调用done函数。

done函数是readStream里面需要重点关注的函数,代码如下:

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230916130346.png

1,将complete标记为true,表示这个流已经处理完了。

2,判断done函数的调用环境,如果是在同步代码块,则使用process.nextTick延迟invokeCallback函数的调用。
done函数同步调用是在这几个地方:

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915224353.png

异步调用则是在stream.on事件的几个回调函数中,并在212行标记为异步:

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915224423.png

3,根据done函数第一个参数判断是否有错误,如果有错误则调用halt方法处理streamhalt 方法会提前结束stream的读取操作。

halt函数内容如下:

https://raw.githubusercontent.com/AC-greener/blog-image/main/20230915224452.png

unpipe(stream) 会断开与这个流相关的其他管道,如果有其他流正在处理 stream 输出的数据,这些流不会收到来自 stream 的数据。

pause 方法是 Nodejs 可读流的一个方法,用于将流暂停,停止触发 data 事件,不再传递数据。

总结

本文我们了解了raw-body的简单使用,raw-body模块也是Nodejs生态中使用的很频繁的一个模块,通过对v0.0.3和v2.5.2版本源码的解析,也了解了内部实现。

参考资料

https://github.com/stream-utils/raw-body

https://nodejs.cn/dist/latest-v18.x/docs/api/stream.html

https://nodejs.cn/dist/latest-v18.x/docs/api/buffer.html
在这里插入图片描述

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

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

相关文章

【LeetCode-简单题KMP】232. 用栈实现队列

文章目录 题目方法一:用输入栈和输出栈模拟队列 题目 方法一:用输入栈和输出栈模拟队列 只有输出栈为空的时候才能将输入栈的元素补充到输出栈,否则输出栈不为空,如果再从输入栈往输出栈填充元素,就会弄乱队列的先进先…

【SpringMVC】拦截器JSR303的使用

【SpringMVC】拦截器&JSR303的使用 1.1 什么是JSR3031.2 为什么使用JSR3031.3 常用注解1.4 Validated与Valid区别1.5 JSR快速入门1.5.2 配置校验规则# 1.5.3 入门案例二、拦截器2.1 什么是拦截器2.2 拦截器与过滤器2.3 应用场景2.4 拦截器快速入门2.5.拦截器链2.6登录案列权…

接口测试——接口协议抓包分析与mock_L1

目录: 接口测试价值与体系常见的接口协议接口测试用例设计postman基础使用postman实战练习 1.接口测试价值与体系 接口测试概念 接口:不同的系统之间相互连接的部分,是一个传递数据的通道接口测试:检查数据的交换、传递和控制…

设计模式之职责链模式

职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 这里发出这个请求的客户端并不知道这当中的哪一个对象最终处理这个请求&am…

TCP详解之三次握手和四次挥手

TCP详解之三次握手和四次挥手 1. TCP基本认识 1.1 什么是 TCP TCP是面向连接的、可靠的、基于字节流的传输层通信协议。 1.2 TCP协议段格式 我们先来看看TCP首部协议的格式 我们先来介绍一些与本文关联比较大的字段,其他字段不做详细阐述。 序列号&#xff1a…

2023面试知识点一

1、新生代和老年代的比例 默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) 1/3 的堆空间大小。老年代 ( Old ) 2/3 的堆空间大小。其中,新生代 ( …

213. 打家劫舍 II

文章目录 Tag题目来源题目解读解题思路方法一:动态规划 写在最后 Tag 【动态规划】【数组】 题目来源 213. 打家劫舍 II 题目解读 你是一个专业的小偷,现在要偷一排屋子,但是你不能偷相邻的两间屋子(这一排房子的首尾是相连的&…

什么是性能调优?方法有哪些?流程是怎样的?

一、性能调优的含义 性能调优通俗来讲就是对计算机硬件、操作系统和应用有相当深入的了解,调节三者之间的关系,实现整个系统(包括硬件、操作系统、应用)的性能最大化,并能不断的满足现有的业务需求。 在判定软件存在…

Hadoop-Hbase

1. Hbase安装 1.1 安装zookeeper、 hbase 解压至/opt/soft,并分别改名 配置环境变量并source生效 #ZK export ZOOKEEPER_HOME/opt/soft/zk345 export PATH$ZOOKEEPER_HOME/bin:$PATH #HBASE_HOME export HBASE_HOME/opt/soft/hbase235 export PATH$HBASE_HOME/b…

浅显易懂理解傅里叶变换

说起电子硬件专业,那不得不提的就是傅里叶变换了。 大学课程中应该吓倒了很多人,谈傅里叶色变了。 本次就来重新认识一下电子硬件中的傅里叶变化。 首先理解之前,当然是需要先知道傅里叶这位大牛的人物百科啦。 傅里叶是法国数学家&#xff0…

【集成学习】对已训练好的模型进行投票

在不同的数据预处理情况下训练得到了三个SVM模型,结果都差不多,对这三个模型的分类结果进行投票 1、三个模型的model_path # 最终model的path self.model_path log_path/model_name_model.gz self.time_log log_path/model_name_time_log.csv# 模型1…

无涯教程-JavaScript - EXP函数

描述 EXP函数返回e升至数字的幂。常数e等于自然对数的底数2.71828182845904。 语法 EXP (number)争论 Argument描述Required/OptionalNumberThe exponent applied to the base e.Required Notes 要计算其他碱基的幂,请使用幂运算符(^) EXP是LN的倒数,LN是数字的自然对数…

免费:CAD批量转PDF工具,附下载地址

分享一款CAD 批量转PDF、打印的工具插件。能自动识别图框大小、自动识别比例、自动编号命名。重点!重点!重点!自动将CAD的多张图纸一次性地、批量地转为PDF,或者打印。效果看下图: 适用环境: 32位系统 Auto…

5-2 Pytorch中的模型层layers

深度学习模型一般由各种模型层组合而成。 torch.nn中内置了非常丰富的各种模型层。它们都属于nn.Module的子类,具备参数管理功能。 例如: nn.Linear, nn.Flatten, nn.Dropout, nn.BatchNorm2d, nn.Embedding nn.Conv2d,nn.AvgPool2d,nn.Conv1d,nn.ConvTr…

layui框架学习(45: 工具集模块)

layui的工具集模块util支持固定条、倒计时等组件,同时提供辅助函数处理时间数据、字符转义、批量事件处理等操作。   util模块中的fixbar函数支持设置固定条(2.7版本的帮助文档中叫固定块),是指固定在页面一侧的工具条元素&…

小程序引入vant-Weapp保姆级教程及安装过程的问题解决

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。 本文同时参与 「掘力星计划」,赢取创作大礼包,挑战创作激励金 当你想在小程序里引入vant时,第一步:打开官方文档,第二步&#xff…

RK3399平台开发系列讲解(入门篇)VIM的基础命令

🚀返回专栏总目录 文章目录 一、Vim 命令速查二、其他命令三、Vim模式沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将介绍Vim相关命令。 一、Vim 命令速查 简单说明一下,这张图上展示了一个键盘。图中的“•”表示,单个字母不是完整的命令,必须再有进一步…

elasticsearch5-RestAPI操作

个人名片: 博主:酒徒ᝰ. 个人简介:沉醉在酒中,借着一股酒劲,去拼搏一个未来。 本篇励志:三人行,必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》,SpringCloud…

typeScript 类型推论

什么是类型推论? 类型推论是 TypeScript 中的一个特性,它允许开发人员不必显式地指定变量的类型。相反,开发人员可以根据变量的使用情况让 TypeScript 编译器自动推断出类型。例如,如果开发人员将一个字符串赋值给一个变量&#…

【大数据】Neo4j 图数据库使用详解

目录 一、图数据库介绍 1.1 什么是图数据库 1.2 为什么需要图数据库 1.3 图数据库应用领域 二、图数据库Neo4j简介 2.1 Neo4j特性 2.2 Neo4j优点 三、Neo4j数据模型 3.1 图论基础 3.2 属性图模型 3.3 Neo4j的构建元素 3.3.1 节点 3.3.2 属性 3.3.3 关系 3.3.4 标…