【express】中间件

news2024/11/25 18:32:55

中间件(Middleware),特指业务流程的中间处理环节

1、调用流程

当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理

2、格式

Express的中间件,本质上就是一个function处理函数,Express中间件的格式如下:

app.get('/',(req,res,next) => {
	next()
})

中间件的形参列表中,必须包含next参数,而路由处理函数只包含 req 和 res

3、next函数的作用

next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由

4、定义中间件函数

const vm = function(req,res,next) {
  console.log('这是最简单的中间件函数');
  // 把流转关系转交给下一个中间件或路由
  next()
}

5、全局生效的中间件

客户端发起的任何请求,到达服务器之后,都会触发的中间件

通过app.use(中间件函数),即可定义一个全局生效的中间件

const vm = function(req,res,next) {
  console.log('这是最简单的中间件函数');
  // 把流转关系转交给下一个中间件或路由
  next()
}
app.use(vm)

6、中间件的作用

多个中间件之间共享一份reqres。可以在上游的中间件中,统一为 req 或 res 对象添加自定义的属性或方法,供下游的中间件或路由进行使用。

在这里插入图片描述

7、定义多个全局中间件

可以使用app.use()连续定义多个全局中间件。客户请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用

app.use(function(req,res,next) {
  console.log('调用了第一个中间件');
  next()
})
app.use(function(req,res,next) {
  console.log('调用了第二个中间件');
  next()
})
app.get('/',(req,res) => {
  res.send('OK.')
})

当请求http://127.0.0.1/

在这里插入图片描述

8、局部生效的中间件

不使用 app.use()定义的中间件为局部生效的中间件

const vm = function(req,res,next) {
  console.log('调用了局部生效的中间件');
  next()
}
app.get('/',vm,(req,res) => {
  res.send('Home Page.')
})
app.get('/user',vm,(req,res) => {
  res.send('User Page.')
})

vm中间件只会在/路由中生效,不会影响其他的路由。当请求http://127.0.0.1/时控制台有打印,请求http://127.0.0.1/user时,控制台没有打印。

9、定义多个局部中间件

app.get('/',mw1,mw2,(req,res) => {})
app.get('/',[mw1,mw2],(req,res) => {})

如上两种写法是等价的

10、了解中间件的注意事项

① 一定要在路由之前注册中间件
② 客户端发过来的请求,可以连续调用多个中间件进行处理
③ 执行完中间件的业务代码之后,要调用 next() 函数
④ 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码
⑤ 连续调用多个中间件时,多个中间件之间共享 req 和 res 对象

11、中间件的分类

1、应用级别的中间件

通过 app.use() 或 app.get() 或 app.post(),绑定到 app 实例上的中间件

2、路由级别的中间件

绑定到 express.Router()路由实例上的中间件

var app = express()
var router = express.Router()
router.use(function(req,res,next) {})
3、错误级别的中间件

专门用来捕获整个项目中发生的异常信息,从而防止项目异常崩溃的问题

格式:function处理函数中,必须有4个形参,从前到后分别是(err、req、res、next)

app.get('/',(req,res) => {
	throw new Error('服务器内部发生了错误!')
	res.send('Home Page.')
})

此时通过http://127.0.0.1/访问服务器,服务器就发生了崩溃

// 定义错误级别的中间件捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err,req,res,next) => {
	res.send('Error:' + err.message)
})

此时通过http://127.0.0.1/访问服务器,服务器虽然发生了错误,但是没有崩溃

注意:错误级别的中间件必须注册在所有路由之后

4、Express内置的中间件

自 Express 4.16.0 版本开始,Express内置了3个常用的中间件,极大的提高了Express项目的开发效率和体验

express.static

快速托管静态资源的内置中间件,例如:HTML文件、图片、CSS样式等(无兼容性)

express.json

解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0+版本中可用)

app.post('/user',(req,res) => {
	// 通过 req.body 来接收客户端发送过来的请求体数据
	console.log(req.body)
	res.send('ok')
})

访问 http://127.0.0.1/user,并使用post请求发送JSON格式的参数,如 {"name":"zs","age":18}
如上打印 req.body 值为 undefined

app.use(express.json())
app.post('/user',(req,res) => {
	console.log(req.body)
	res.send('ok')
})

配置了解析JSON格式的请求体数据的中间件,打印 req.body 值为 {name:'zs',age:'18'}

express.urlencoded

解析 URL-encoded 格式的请求体数据(有兼容性,仅在 4.16.0+版本中可用)

app.post('/user',(req,res) => {
	console.log(req.body)
	res.send('ok')
})

访问 http://127.0.0.1/user,并使用post请求发送x-www-form-urlencoded格式的参数,如 key:name,value:zskey:age,value:18,如上打印 req.body 值为 undefined

app.use(express.json())
app.post('/user',(req,res) => {
	console.log(req.body)
	res.send('ok')
})

配置的是解析JSON格式的请求体数据的中间件,打印 req.body 值为 {}

app.use(express.urlencoded({extended:false}))
app.post('/user',(req,res) => {
	console.log(req.body)
	res.send('ok')
})

配置了解析表单中 urlencoded 格式的请求体数据的中间件,打印 req.body 值为 {name:'zs',age:'18'}

5、第三方中间件

非 Express 官方内置的,而是由第三方开发出来的中间件为第三方中间件。可以按需下载并配置,从而提高项目的开发效率

body-parser这个第三方中间件用来解析请求体数据

① 运行 npm i body-parser
② 使用 require导入中间件
③ 调用app.use()注册并使用中间件

const parser = require('body-parser')
app.use(parser.urlencoded({extended:false}))

注意:Express内置的express.urlencoded中间件,就是基于body-parser这个第三方中间件进一步封装出来的

6、自定义中间件

手动模拟一个类似于 express.urlencoded 的中间件,来解析POST提交到服务器的表单数据

① 定义中间件
② 监听 req 的 data 事件
③ 监听 req 的 end 事件
④ 使用 querystring 模块解析请求体数据
⑤ 将解析出来的数据对象挂载为 req.body
⑥ 将自定义中间件封装为模块

1、定义中间件

app.use((req,res,next) => {})

2、监听 req 的 data 事件

监听 req 对象的 data 事件来获取客户端发送到服务器的数据。
如果数据量比较大无法一次性发送完毕,则客户端会把数据切割后分批发送到服务器,每次触发 data 事件获取到的数据只是完整数据的一部分,需要手动对接收的数据进行拼接

app.use((req,res,next) => {
  // 1. 定义一个变量,专门用来存储客户端发送过来的请求体数据
  let str = ''
  // 2. 监听req的data事件
  req.on('data',(chunk) => {
    str += chunk
  })
})

3、监听 req 的 end 事件

当请求体数据接收完毕之后,会自动触发 req 的 end 事件

app.use((req,res,next) => {
  let str = ''
  req.on('data',(chunk) => {
    str += chunk
  })
  // 3.监听req的end事件
  req.on('end',() => {
    // 在str中存放的是完整的请求体数据
    console.log(str);
  })
})

在这里插入图片描述
得到的值为:name=zs&age=18&gender=%E7%94%B7

4、使用 querystring 模块解析请求体数据

Node.js 内置了一个 querystring 模块,专门用来处理查询字符串。通过这个模块提供的 parse() 函数,可以把查询字符串解析成对象的格式

const qs = require('querystring')
app.use((req,res,next) => {
  let str = ''
  req.on('data',(chunk) => {
    str += chunk
  })
  req.on('end',() => {
    // 4.把字符串格式的请求体数据解析成对象格式
    const body = qs.parse(str)
    console.log(body)
  })
})

得到的值为:{ name: 'zs', age: '18', gender: '男' }

5、将解析出来的数据对象挂载为 req.body

上游的中间件和下游的中间件及路由之间共享同一份 reqres。因此可以将解析出来的数据挂载为 req 的自定义属性,命名为 req.body 供下游使用

const qs = require('querystring')
app.use((req,res,next) => {
  let str = ''
  req.on('data',(chunk) => {
    str += chunk
  })
  req.on('end',() => {
    const body = qs.parse(str)
    // 5. 将解析出来的请求体对象挂载为 req.body 属性,最后要调用 next() 函数执行后续的业务逻辑
    req.body = body
    next()
  })
})

6、将自定义中间件封装为模块

把自定义的中间件函数封装为独立的模块 custom-body-parser.js

const qs = require('querystring')
const bodyParser = function(req,res,next) {
  let str = ''
  req.on('data',(chunk) => {
    str += chunk
  })
  req.on('end',() => {
    const body = qs.parse(str)
    req.body = body
    next()
  })
}
module.exports = bodyParser

使用:

// 导入自己封装的中间件模块
const customBodyParser = require('./custom-body-parser')
// 将自定义的中间件函数注册为全局可用的中间件
app.use(customBodyParser)

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

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

相关文章

repeat语句 及 赋值语句说明---verilog HDL

参考:verilog数字系统设计教程【第四版】夏宇闻 repeat语句用阻塞赋值语句,与用非阻塞语句产生的结果差别非常大,所以将二者放在同一篇文章中。 1、赋值语句 2、repeat 语句介绍   2.1、用法要点   2.2、代码举例    代码1:…

2023年北向L2接口的发展会怎么样?

众所周知北向L2接口的逐笔成交功能可以精确查看每笔成交,跟踪北向资金动向,那么由于北向资金动向是股市行情的晴雨表,因此股民做股票投资是要时刻关注着北向资金流动方向的,那么北向L2接口作为帮助头者提供跟踪资金动向的服务软件…

浅谈撮合引擎

浅谈撮合引擎设计撮合引擎简介撮合引擎的发展币安中小型交易所小型交易所业务交易流程竞价方式交易所常用指令开发简易架构设计撮数据结构设计交易委托账本限价委托单其它委托单关键代码实现1.创建一个ringbuffer2. 设置事件监听4.订单撮合主逻辑撮合分支processMath函数逻辑PS…

uniapp实现iOS支付苹果内购支付踩过的坑以及具体操作步骤

由于我们app会员属于虚拟产品,所以苹果商店要求我们必须选择苹果内购,否则就勒令下架。 无奈,于是就又开始了踩坑之旅~ uniapp可以直接使用uni-pay的插件去进行苹果内购。 但是,在对接自己的项目之前,建议先跑通示例项…

JavaEE-Spring(Spring中的五大类注解,@Bean注解,对象装配(@Autowired,@Resource),Bean对象在Spring中的作用域)

文章目录1. 配置扫描路径2. Spring五大类注解3. Spring Bean注解对象装配4. Bean对象在Spring中的作用域5. Bean生命周期1. 配置扫描路径 只有设置了扫描路径&#xff0c;其他的路径下注解不会被Spring扫描 这里设置路径为com.beans下 <?xml version"1.0" enc…

(七)devops持续集成开发——jenkins流水线发布一个node环境下的前端vue项目

前言 在前面的章节中已经介绍了jenkins集成前端流水化部署环境的内容&#xff0c;本节内容是关于前端项目的流水化部署发布&#xff0c;通过实操发布一个前端项目&#xff0c;从而完成前端项目的流水化发布。前端项目主要是静态资源的发布&#xff0c;这里我们以一个vue项目为…

智慧物流信息化供应链管理体系转型发展现状

现如今&#xff0c;伴随着时代的迅速发展和高新科技水准的持续提升&#xff0c;人们慢慢进入了信息时代。在其中&#xff0c;物流制造行业也从以往20年前的粗放型管理机制慢慢变化为信息化、智慧化的管理机制。 5G、云计算技术、AI、物联网等新技术的出现加快了各个领域经营方法…

k线图中的三条线是什么?

新手投资朋友可能会在行情软件中发现&#xff0c;图表中除了K线以外&#xff0c;其下方还有三条颜色不一样的曲线&#xff0c;到底这三条线有什么功能呢&#xff1f;它们的使用方法又是怎样的呢&#xff1f; 其实&#xff0c;这三条线分别是短、中、长周期移动平均线&#xff0…

界面控件DevExpress WinForm——属于WinForm组件的MVVM框架

DevExpress WinForm拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForm能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜任…

谷粒学院——Day17【数据同步工具、SpringCloud【GateWay网关】、权限管理功能(接口)】

❤ 作者主页&#xff1a;Java技术一点通的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是Java技术一点通&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得关注、点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习&#xff0c;共同进步&#xff01;&am…

sql调优

一、MySQL架构总览&#xff1a; 三、SQL解析顺序 SELECT DISTINCT< select_list > FROM< left_table > < join_type > JOIN < right_table > ON < join_condition > WHERE< where_condition > GROUP BY< group_by_list > HAVING<…

振弦采集模块的频率值与温度值的修正

振弦采集模块的频率值与温度值的修正 此功能在 SF3.51 版本时增加。 固件版本 V3.51 修改固件版本号为 V3.51_2200827。 增加了频率和温度的多项式修正参数和对应指令。 $STFP、 $GTFP、 $STTP、 $GTTP 增加了 FFT 频幅数据输出功能。设置 ATSD_SEL.[5]为 1。 修正了 VM608 采集…

java实现的非关系型数据库:nosqldb

nosqldb一、nosqldb介绍二、nosqldb功能介绍三、数据存储结构介绍1. 数据文件存储结构(data.nosqldb)2.索引文件存储结构(index.mbdb)三、优化点1. 不支持连表查询2. 不支持分片存储3. 碎片整理一、nosqldb介绍 github地址 https://github.com/MaBo2420935619/nosqldb nosqld…

2022年终总结2023年计划

目录 前言&#xff1a; 2022年总结&#xff1a; 工作上&#xff1a; 生活上&#xff1a; 2023年规划&#xff1a; 工作上&#xff1a; 生活上&#xff1a; 前言&#xff1a; 嗨&#xff0c;不知不觉一年又过去了&#xff0c;2022已经结束了&#xff0c;我们迎来了2023。…

c++ -- STL容器--list容器

7. list容器7.1 简介① 功能&#xff1a;将数据进行链式存储。② 链表(list)是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的。③ 链表的组成&#xff1a;链表由一系列结点组成。④ 结点的组成&#xff1a;一个是存储数据元素…

serverless论文总结

1.Benchmarking, Analysis, and Optimization of Serverless Function Snapshots https://zhuanlan.zhihu.com/p/572288442 这项工作引入了vHive&#xff0c;一个针对无服务器实验的开源框架&#xff0c;它使系统研究人员能够在整个无服务器堆栈中进行创新。vHive集成了来自领…

ITSM | 权威指南发布,高速IT服务管理团队是什么样子的?

当Netflix正在打造流媒体平台时&#xff0c;还有人在营业厅里为一张网卡而烦恼。当Craig Newmark创建免费分类广告网站时&#xff0c;报社的网络管理员最关心的还是重启电子邮件服务器。当数字化转型成为一道必答题&#xff0c;谁能率先给出解题之法&#xff1f; 阅读本篇文章&…

Input子系统

文章目录前言Input子系统简介Input子系统代码实现框架Linux Input子系统支持的数据类型input核心层设备驱动层input_allocate_device 与 函数input_set_capabilityinput_register_device 函数input_unregister_device 与 input_free_device 函数事件处理层input_attach_handler…

A. Divide and Conquer

An array bb is good if the sum of elements of bb is even. You are given an array aa consisting of nn positive integers. In one operation, you can select an index ii and change ai:⌊ai2⌋ai:⌊ai2⌋. †† Find the minimum number of operations (possibly 00)…

ArcGIS 切片问题小结

1. 如果发布的切片缓存服务没有自动启动怎么办&#xff1f; 在进行切片时偶然情况下可能会遇到&#xff0c;你在切片时已经设置了server自动进行切片处理&#xff0c;但是在服务发布后&#xff0c;服务发布成功&#xff0c;但是服务没有成功启动&#xff0c;导致服务器没有自动…