NodeJs Express中间件超详细讲解

目录
  • 什么是中间件
  • 现实生活中的例子
  • Express 中间件的调用流程
  • Express 中间件的格式
  • next 函数的作用
  • 定义中间件函数
  • 全局生效的中间件
  • 定义全局中间件的简化形式
  • 中间件的作用
  • 定义多个全局中间件
  • 局部生效的中间件
  • 定义多个局部中间件
  • 了解中间件的5个使用注意事项
  • 中间件的分类
    • 应用级别的中间件
    • 路由级别的中间件
    • 错误级别的中间件
    • Express内置的中间件
    • 第三方的中间件
  • 自定义中间件
    • 1. 需求描述与实现步骤
    • 2. 定义中间件
    • 3 .监听req的data事件
    • 4. 监听req的end事件
    • 5. 使用Qs模块解析请求体数据
    • 6. 将解析出来的数据对象挂载为req.body
    • 7. 将自定义中间件封装为模块
    • 封装自定义中间件

什么是中间件

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

现实生活中的例子

在处理污水的时候,一般都要经过三个处理环节,从而保证处理过后的废水,达到排放标准

处理污水的这三个中间处理环节,就可以叫做中间件。

Express 中间件的调用流程

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

Express 中间件的格式

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

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

next 函数的作用

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

定义中间件函数

可以通过如下的方式,定义一个最简单的中间件函数:

全局生效的中间件

客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。

通过调用 app.use(中间件函数),即可定义一个全局生效的中间件,示例代码如下:

定义全局中间件的简化形式

const express = require('express')
const app = express()
// // 定义一个最简单的中间件函数
// const mw = function (req, res, next) {
//   console.log('这是最简单的中间件函数')
//   // 把流转关系,转交给下一个中间件或路由
//   next()
// }
// // 将 mw 注册为全局生效的中间件
// app.use(mw)
// 这是定义全局中间件的简化形式
app.use((req, res, next) => {
  console.log('这是最简单的中间件函数')
  next()
})
app.get('/', (req, res) => {
  console.log('调用了 / 这个路由')
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  console.log('调用了 /user 这个路由')
  res.send('User page.')
})
app.listen(80, () => {
  console.log('http://127.0.0.1')
})

当在apifox请求这两个请求时,都会先调用这个中间件再返回请求的内容。

中间件的作用

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

const express = require('express')
const app = express()
// 这是定义全局中间件的简化形式
app.use((req, res, next) => {
  // 获取到请求到达服务器的时间
  const time = Date.now()
  // 为 req 对象,挂载自定义属性,从而把时间共享给后面的所有路由
  req.startTime = time
  next()
})
app.get('/', (req, res) => {
  res.send('Home page.' + req.startTime)
})
app.get('/user', (req, res) => {
  res.send('User page.' + req.startTime)
})
app.listen(80, () => {
  console.log('http://127.0.0.1')
})

定义多个全局中间件

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

const express = require('express')
const app = express()
// 定义第一个全局中间件
app.use((req, res, next) => {
  console.log('调用了第1个全局中间件')
  next()
})
// 定义第二个全局中间件
app.use((req, res, next) => {
  console.log('调用了第2个全局中间件')
  next()
})
// 定义一个路由
app.get('/user', (req, res) => {
  res.send('User page.')
})
app.listen(80, () => {
  console.log('http://127.0.0.1')
})

局部生效的中间件

不使用 app.use() 定义的中间件,叫做局部生效的中间件,示例代码如下:

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义中间件函数
const mw1 = (req, res, next) => {
  console.log('调用了局部生效的中间件')
  next()
}
// 2. 创建路由,mw1只在当前路由生效
app.get('/', mw1, (req, res) => {
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  res.send('User page.')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

定义多个局部中间件

可以在路由中,通过如下两种等价的方式,使用多个局部中间件:

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义中间件函数
const mw1 = (req, res, next) => {
  console.log('调用了第一个局部生效的中间件')
  next()
}
const mw2 = (req, res, next) => {
  console.log('调用了第二个局部生效的中间件')
  next()
}
// 2. 创建路由
app.get('/', [mw1, mw2], (req, res) => {
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  res.send('User page.')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

了解中间件的5个使用注意事项

① 一定要在路由之前注册中间件

② 客户端发送过来的请求,可以连续调用多个中间件进行处理

③ 执行完中间件的业务代码之后,不要忘记调用 next() 函数

④ 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码

⑤ 连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象

中间件的分类

为了方便大家理解和记忆中间件的使用,Express 官方把常见的中间件用法,分成了 5 大类,分别是:

① 应用级别的中间件

② 路由级别的中间件

③ 错误级别的中间件

④ Express 内置的中间件

⑤ 第三方的中间件

应用级别的中间件

通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件,代码示例如下:

路由级别的中间件

绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上,代码示例如下:

错误级别的中间件

错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。

格式:错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。

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

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义路由
app.get('/', (req, res) => {
  // 1.1 人为的制造错误
  throw new Error('服务器内部发生了错误!')
  res.send('Home page.')
})
// 2. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err, req, res, next) => {
  console.log('发生了错误!' + err.message)
  res.send('Error:' + err.message)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

Express内置的中间件

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

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

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

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

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 注意:除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
// 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据
app.use(express.json())
// 通过 express.urlencoded() 这个中间件,来解析 表单中的 url-encoded 格式的数据
app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
  // 在服务器,可以使用 req.body 这个属性,来接收客户端发送过来的请求体数据
  // 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined
  console.log(req.body)
  res.send('ok')
})
app.post('/book', (req, res) => {
  // 在服务器端,可以通过 req,body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据
  console.log(req.body)
  res.send('ok')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

第三方的中间件

非 Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,大家可以按需下载并配置

第三方中间件,从而提高项目的开发效率。

例如:在 express@4.16.0 之前的版本中,经常使用 body-parser 这个第三方中间件,来解析请求体数据。使用步

骤如下:

① 运行 npm install body-parser 安装中间件

② 使用 require 导入中间件

③ 调用 app.use() 注册并使用中间件

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

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 导入解析表单数据的中间件 body-parser
const parser = require('body-parser')
// 2. 使用 app.use() 注册中间件
app.use(parser.urlencoded({ extended: false }))
// app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
  // 如果没有配置任何解析表单数据的中间件,则 req.body 默认等于 undefined
  console.log(req.body)
  res.send('ok')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

自定义中间件

1. 需求描述与实现步骤

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

实现步骤:

① 定义中间件

② 监听 req 的 data 事件

③ 监听 req 的 end 事件

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

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

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

2. 定义中间件

使用 app.use() 来定义全局生效的中间件,代码如下:

3 .监听req的data事件

在中间件中,需要监听 req 对象的 data 事件,来获取客户端发送到服务器的数据。

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

4. 监听req的end事件

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

因此,我们可以在 req 的 end 事件中,拿到并处理完整的请求体数据。示例代码如下:

5. 使用Qs模块解析请求体数据

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

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

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

7. 将自定义中间件封装为模块

为了优化代码的结构,我们可以把自定义的中间件函数,封装为独立的模块,示例代码如下:

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 导入 Node.js 内置的 Qs 模块
const qs = require('Qs')
// 这是解析表单数据的中间件
app.use((req, res, next) => {
  // 定义中间件具体的业务逻辑
  // 1. 定义一个 str 字符串,专门用来存储客户端发送过来的请求体数据
  let str = ''
  // 2. 监听 req 的 data 事件
  req.on('data', (chunk) => {
    str += chunk
  })
  // 3. 监听 req 的 end 事件
  req.on('end', () => {
    // 在 str 中存放的是完整的请求体数据
    // console.log(str)
    // TODO: 把字符串格式的请求体数据,解析成对象格式
    const body = qs.parse(str)
    req.body = body
    next()
  })
})
app.post('/user', (req, res) => {
  res.send(req.body)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

封装自定义中间件

// 导入 Node.js 内置的 Qs 模块
const qs = require('Qs')
const bodyParser = (req, res, next) => {
  // 定义中间件具体的业务逻辑
  // 1. 定义一个 str 字符串,专门用来存储客户端发送过来的请求体数据
  let str = ''
  // 2. 监听 req 的 data 事件
  req.on('data', (chunk) => {
    str += chunk
  })
  // 3. 监听 req 的 end 事件
  req.on('end', () => {
    // 在 str 中存放的是完整的请求体数据
    // console.log(str)
    // TODO: 把字符串格式的请求体数据,解析成对象格式
    const body = qs.parse(str)
    req.body = body
    next()
  })
}
module.exports = bodyParser
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 导入自己封装的中间件模块
const customBodyParser = require('./14.custom-body-parser')
// 2. 将自定义的中间件函数,注册为全局可用的中间件
app.use(customBodyParser)
app.post('/user', (req, res) => {
  res.send(req.body)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

到此这篇关于NodeJs Express中间件超详细讲解的文章就介绍到这了,更多相关NodeJs Express中间件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Nodejs之Express中间件的分类介绍

    目录 中间件的分类 分别是 1 应用级别的中间件 2 路由级别的中间件 3 错误级别的中间件 4 Express内置的中间件 5 第三方中间件 总结 中间件的分类 为了方便大家理解和记忆中间件的使用,Express官方把常见的中间件用法,分成了5大类 分别是 应用级别的中间件 路由级别的中间件 错误级别的中间件 Express内置的中间件 第三方的中间件 1 应用级别的中间件 通过app.use()或app.get()或app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,代码

  • nodejs关于中间件解析

    目录 nodejs 中间件 中间件的编写 中间件的使用 nodejs制作自定义中间件 总结 nodejs 中间件 今天我们来了解一下 nodejs 中间件,中间件可以说是 node 中最重要也是最核心的地方.它最重要的思想便是 洋葱模型 ,从外到内依次执行中间件. 说了半天中间件到底有什么作用呢,在处理服务请求时,我们要进行很多细节的处理,比如解析请求体,报文报头,设置 cookie 等等很多操作,如果每次访问路由都要编写这些繁琐的代码,将会造成代码混乱,你可能连自己的业务逻辑代码都找不到了.

  • nodeJs编写错误处理中间件问题

    目录 nodeJs编写错误处理中间件 Connect中间件之错误处理中间件 范例 多个错误处理中间件组件 nodeJs编写错误处理中间件 app.use(async(ctx, next) => { try { await next(); } catch (err) { console.log(err, "错误机制"); ctx.status = err.status || err.statusCode; ctx.body = { message: err.message }; }

  • Nodejs中Express 常用中间件 body-parser 实现解析

    写在前面 body-parser是非常常用的一个express中间件,作用是对post请求的请求体进行解析.使用非常简单,以下两行代码已经覆盖了大部分的使用场景. app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); 本文从简单的例子出发,探究body-parser的内部实现.至于body-parser如何使用,感兴趣的同学可以参考官方文档. 入门基础 在正式讲解前,我们先来看一个POST

  • NodeJs Express中间件超详细讲解

    目录 什么是中间件 现实生活中的例子 Express 中间件的调用流程 Express 中间件的格式 next 函数的作用 定义中间件函数 全局生效的中间件 定义全局中间件的简化形式 中间件的作用 定义多个全局中间件 局部生效的中间件 定义多个局部中间件 了解中间件的5个使用注意事项 中间件的分类 应用级别的中间件 路由级别的中间件 错误级别的中间件 Express内置的中间件 第三方的中间件 自定义中间件 1. 需求描述与实现步骤 2. 定义中间件 3 .监听req的data事件 4. 监听r

  • Redis缓存实例超详细讲解

    目录 1 前言 1.1 什么是缓存 1.2 缓存的作用及成本 1.3 Redis缓存模型 2 给商户信息添加缓存 3 缓存更新策略 3.1 更新策略介绍 3.2 主动更新策略 3.3 主动更新策略练习 4 缓存穿透及其解决方案 4.1 缓存穿透的概念 4.2 解决方案及实现 5 缓存雪崩的概念及其解决方案 6 缓存击穿及解决方案 6.1 什么是缓存击穿 6.2 缓存击穿解决方法 6.2.1 互斥锁 6.2.2 逻辑过期 1 前言 1.1 什么是缓存 缓存就是数据交换的缓冲区(称作Cache [

  • 前端需知nodejs express中间件使用及定义详解

    目录 引言 中间件的定义 中间件的使用 总结 引言 在上一节中我们简单了解了express的路由,本文主要分析下express的另一个核心功能中间件的使用.路由是express整个框架能有如今广泛使用和发展的基础,那中间件就是其发展的养分.正是express丰富的中间件才节省了我们大量的开发时间. 中间件的定义 对于express来说,中间件其实就是一个规定了入参和出参的JavaScript函数,这个函数可以处理请求request和返回response相关的参数,其功能如下: 执行任何代码. 处

  • RocketMQ生产消息与消费消息超详细讲解

    目录 1 RocketMQ简介 2 MQ的常见产品 3 环境搭建 4 单生产者单消费者模式 5 单生产者多消费者模式 5.1默认模式(负载均衡) 5.2广播模式 6 多生产者多消费者模式 1 RocketMQ简介 RocketMQ是阿里开源的一款非常优秀中间件产品,脱胎于阿里的另一款队列技术MetaQ,后捐赠给Apache基金会作为一款孵化技术,仅仅经历了一年多的时间就成为Apache基金会的顶级项目.并且它现在已经在阿里内部被广泛的应用,并且经受住了多次双十一的这种极致场景的压力(2017年的

  • java反射超详细讲解

    目录 Java反射超详解✌ 1.反射基础 1.1Class类 1.2类加载 2.反射的使用 2.1Class对象的获取 2.2Constructor类及其用法 2.4Method类及其用法 Java反射超详解✌ 1.反射基础 Java反射机制是在程序的运行过程中,对于任何一个类,都能够知道它的所有属性和方法:对于任意一个对象,都能够知道它的任意属性和方法,这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制. Java反射机制主要提供以下这几个功能: 在运行时判断任意一个对象所属

  • 超详细讲解Linux C++多线程同步的方式

    目录 一.互斥锁 1.互斥锁的初始化 2.互斥锁的相关属性及分类 3,测试加锁函数 二.条件变量 1.条件变量的相关函数 1)初始化的销毁读写锁 2)以写的方式获取锁,以读的方式获取锁,释放读写锁 四.信号量 1)信号量初始化 2)信号量值的加减 3)对信号量进行清理 背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通过多线程模拟多窗口售票为例: #include <iostream> #include<pthread.h> #include<stdio.h&

  • 超详细讲解Linux DHCP服务

    目录 一.DHCP服务(动态主机配置协议) 1.背景 2.概述 3.优点 4.DHCP报文类型 5.DHCP 的分配方式 二.安装 DHCP 服务器 1.DHCP 服务软件 2.主配置文件 三.配置步骤 1.使用 DHCP 动态的给 PC 机分配 IP 地址 ① eNSP ②虚拟机 ③验证 ④进入命令行"ipconfig"测试 一.DHCP服务(动态主机配置协议) 1.背景 1.手动设置工作量大且容易冲突 2.用DHCP可以减少工作量和避免地址冲突 2.概述 作用:为局域网内的电脑分配

  • 超详细讲解python正则表达式

    目录 正则表达式 1.1 正则表达式字符串 1.1.1 元字符 1.1.2 字符转义 1.1.3 开始与结束字符 1.2 字符类 1.2.1 定义字符类 1.2.2 字符串取反 1.2.3 区间 1.2.4 预定义字符类 1.3 量词 1.3.1 量词的使用 1.3.2 贪婪量词和懒惰量词 1.4 分组 1.4.1 分组的使用 1.4.2 分组命名 1.4.3 反向引用分组 1.4.4 非捕获分组 1.5 re模块 1.5.1 search()和match()函数 1.5.2 findall()

  • 超详细讲解Java线程池

    目录 池化技术 池化思想介绍 池化技术的应用 如何设计一个线程池 Java线程池解析 ThreadPoolExecutor使用介绍 内置线程池使用 ThreadPoolExecutor解析 整体设计 线程池生命周期 任务管理解析 woker对象 Java线程池实践建议 不建议使用Exectuors 线程池大小设置 线程池监控 带着问题阅读 1.什么是池化,池化能带来什么好处 2.如何设计一个资源池 3.Java的线程池如何使用,Java提供了哪些内置线程池 4.线程池使用有哪些注意事项 池化技术

  • 超详细讲解Java异常

    目录 一.Java异常架构与异常关键字 Java异常简介 Java异常架构 1.Throwable 2.Error(错误) 3.Exception(异常) 4.受检异常与非受检异常 Java异常关键字 二.Java异常处理 声明异常 抛出异常 捕获异常 如何选择异常类型 常见异常处理方式 1.直接抛出异常 2.封装异常再抛出 3.捕获异常 4.自定义异常 5.try-catch-finally 6.try-with-resource 三.Java异常常见面试题 1.Error 和 Excepti

随机推荐