可能是全网最详细的express--middleware
写在前面
hello,小伙伴们,我是你们的pubdreamcc,本篇博文出至于我的GitHub仓库node学习教程资料,欢迎小伙伴们点赞和star,你们的点赞是我持续更新的动力。
GitHub仓库地址:node学习教程
好了,废话不多说了,今天继续我们express的学习~
开篇
今天我们来聊一聊express中非常重要的一个概念——express middleware (express中间件)。中间件实质就是一个函数,我们从发送一个请求到最后服务端给回响应,在这个过程中请求会经过多个中间件处理。下面给出一张图来理解express中间件的概念:

这个是我们平常自来水的处理过程图,可以看到我们自来水从水库取出来到最后进入每个家庭,中间经过了多次的处理,我们可以理解为中间的每一个处理过程(方法)称之为中间件。express中middleware类似于上图中的每一个处理过程,我们请求到响应之间也是经过了一个或多个中间件处理,最后才是我们看到的响应结果。
中间件的分类和使用
这里我们可以看下express官网对中间件的分类介绍。
Application-level middleware(应用程序级别中间件)
应用程序级别中间件又分为以下三种情况。
- 不管请求地址和请求方法,任何请求都能进入。
const app = express()
app.use((req, res, next) => {
console.log('请求来了1~')
next() // 调用next(),请求会去下一个紧挨着的匹配的中间件
})
app.use((req, res, next) => {
console.log('请求来了2~')
// 没有调用next(),请求会留在该中间件中
})
app.use((req, res, next) => {
console.log('请求来了3~')
})
这里配置了三个应用程序级别的中间件,当客户端发一个请求,就会从上至下首先进入第一个中间件处理,接着看是否内部调用next()来决定请求是否往下执行。
我们在服务端请求处理函数中还可以传入第三个参数:next,如果在函数体中调用next(),则请求会在经过该中间件处理后继续向下找最近的那个匹配成功的中间件,并且进入该中间件处理。如果没有调用next(),则请求会留在该中间件中,不会继续往下执行。
结果:
// 请求来了1
// 请求来了2
- 特定请求路径的任何类型请求都能进入
const app = express()
app.use('/a', (req, res, next) => {
console.log('请求来了1~')
next() // 调用next(),请求会去下一个紧挨着的匹配的中间件
})
app.use('/b', (req, res, next) => {
console.log('请求来了2~')
})
app.use('/a', (req, res, next) => {
console.log('请求来了3~')
})
同样的,这里也配置了三个中间件,因为在中间件中规定了请求路径,所以只能是以 /a 开头 的请求才能进入第一个中间件处理。因为在第一个中间件内部调用了next()方法,所以请求会继续往下找匹配的中间件,第二个中间请求地址不匹配,所以进入第三个中间件处理。
结果:
// 请求来了1
// 请求来了3
- 特定请求方法和特定请求地址的请求能够进入
对于这种情况我们最为熟悉,之前已经练习过多次。
const app = express()
app.get('/a', (req, res, next) => {
console.log('请求来了1~')
next() // 调用next(),请求会去下一个紧挨着的匹配的中间件
})
app.post('/a', (req, res, next) => {
console.log('请求来了2~')
})
app.get('/a', (req, res, next) => {
console.log('请求来了3~')
})
同样以上面的例子,写三个中间件。这里规定了必须以 GET 请求而且请求路径为:/a的请求会被第一个中间件处理,因为内部调用了 next(),所以请求会继续往下找临近的匹配的中间件,第二个中间件规定的请求方法为:POST,所以进入第三个中间件处理。
结果:
// 请求来了1
// 请求来了3
Router-level middleware(路由级别中间件)
路由级别的中间件与上面应用程序级别中间件的工作方式类似,只不过把路由模块单独提取出来,最后通过把路由容器挂载到 app 服务器实例上即可。
- 不管请求地址和请求方法,任何请求都能进入。
const router = express.Router()
router.use((req, res, next) => {
console.log('请求来了1~')
next() // 调用next(),请求会去下一个紧挨着的匹配的中间件
})
router.use((req, res, next) => {
console.log('请求来了2~')
// 没有调用next(),请求停留在此,不会继续往下找了
})
router.use((req, res, next) => {
console.log('请求来了3~')
})
结果:
// 请求来了1~
// 请求来了2~
- 特定请求路径的任何类型请求都能进入
const router = express.Router()
router.use('/a', (req, res, next) => {
console.log('请求来了1~')
next() // 调用next(),请求会去下一个紧挨着的匹配的中间件
})
router.use('/b', (req, res, next) => {
console.log('请求来了2~')
})
router.use('/a', (req, res, next) => {
console.log('请求来了3~')
})
结果:
// 请求来了1~
// 请求来了3~
- 特定请求方法和特定请求地址的请求能够进入
const router = express.Router()
router.get('/a', (req, res, next) => {
console.log('请求来了1~')
next() // 调用next(),请求会去下一个紧挨着的匹配的中间件
})
router.post('/a', (req, res, next) => {
console.log('请求来了2~')
})
router.get('/a', (req, res, next) => {
console.log('请求来了3~')
})
结果:
// 请求来了1~
// 请求来了3~
Error-handling middleware(全局错误处理中间件)
在实际开发中,往往我们需要统一来处理异步操作发生的错误,这个时候我们需要在项目中配置一个全局错误处理中间件即可。
const express = require('express')
const fs = require('fs')
const app = express()
app.get('/', (req, res, next) => {
fs.readFile('./index.html', (err, date) => {
if (err) {
// 如果异步读取文件错误,把错误对象传给next()
return next(err)
}
// 成功,发送读取后的数据,浏览器渲染页面
res.send(data)
})
})
// 配置一个全局错误处理中间件,注意这里接收四个参数,一个都不能少,否则会出错
app.use((err, req, res, next) => {
// err参数为前面next()方法接收到的错误对象
res.status(500).send(err.message)
})
通过配置一个全局错误处理中间件,我们可以统一处理请求发送错误时服务端的响应。这里一定要给错误处理中间件的处理函数 四个参数, 切记。在应用程序级别的中间件 app.get() 中,当异步读取文件失败时,记得要把错误对象传递给next() 方法 。通过这样,一旦 index.html 文件丢失,请求则会进入错误处理中间件,发送错误对象的具体消息给到浏览器响应。
Built-in middleware(内置中间件)
express为我们也提供了一些内置中间件,比如常见的开放静态资源: express.static()等。
const express = require('express')
const app = express()
// 开放公共资源
app.use('/public/', express.static('./public'))
Third-party middleware(第三方中间件)
同样,我们也可以使用别人封装好的第三方中间件,第三方中间件都是一个个第三方包,所以使用前需要我们单独下载到项目中。之前的 body-parser 中间件,就是用来解析 post 请求体的数据,还有常见的像 express-session 中间件, cookie-parser用来获取请求的 cookie 数据等。第三方中间件具体可以查看 express 官方 API,这里就不再一一罗列 。
官方推荐的第三方中间件:第三方中间件
写在后面
因为文章内容是自己空闲时间编写,如果您发现有哪些错误可以在GitHub上提交 issue ,也可以在留言区发表评论,这样可以方便于后来的同学学习,谢谢啦~
可能是全网最详细的express--middleware的更多相关文章
- 全网最详细的IDEA、Eclipse和MyEclipse之间于Java web项目发布到Tomcat上运行成功的对比事宜【博主强烈推荐】【适合普通的还是Maven方式创建的】(图文详解)
不多说,直接上干货! IDEA [适合公司业务]全网最详细的IDEA里如何正确新建[普通或者Maven]的Java web项目并发布到Tomcat上运行成功[博主强烈推荐](类似eclipse里同一个 ...
- 【适合公司业务】全网最详细的IDEA里如何正确新建【普通或者Maven】的Java web项目并发布到Tomcat上运行成功【博主强烈推荐】(类似eclipse里同一个workspace下【多个子项目】并存)(图文详解)
不多说,直接上干货! 首先,大家要明确,IDEA.Eclipse和MyEclipse等编辑器之间的新建和运行手法是不一样的. 如果是在Myeclipse里,则是File -> new -> ...
- 全网最详细的Eclipse和MyEclipse里对于Java web项目发布到Tomcat上运行成功的对比事宜【博主强烈推荐】【适合普通的还是Maven方式创建的】(图文详解)
不多说,直接上干货! 首先,大家要明确,IDEA.Eclipse和MyEclipse等编辑器之间的新建和运行手法是不一样的. 全网最详细的MyEclipse里如何正确新建普通的Java web项目并发 ...
- 全网最详细的IDEA里如何正确新建普通的Java web项目并发布到Tomcat上运行成功【博主强烈推荐】(类似eclipse里同一个workspace下【一个子项目】并存)(图文详解)
不多说,直接上干货! 首先,大家要明确,IDEA.Eclipse和MyEclipse等编辑器之间的新建和运行手法是不一样的. 如果是在Myeclipse里,则是File -> new -> ...
- 全网最详细的Eclipse里如何正确新建普通的Java web项目并发布到Tomcat上运行成功【博主强烈推荐】(图文详解)
不多说,直接上干货! 首先,大家要明确,IDEA.Eclipse和MyEclipse等编辑器之间的新建和运行手法是不一样的. 如果是在Myeclipse里,则是File -> new -> ...
- 全网最详细的MyEclipse里如何正确新建普通的Java web项目并发布到Tomcat上运行成功【博主强烈推荐】(图文详解)
不多说,直接上干货! 首先,大家要明确,IDEA.Eclipse和MyEclipse等编辑器之间的新建和运行手法是不一样的. 如果是在eclipse里,则是File -> new -> ...
- 全网最详细的Windows系统里Oracle 11g R2 Client(64bit)的下载与安装(图文详解)
不多说,直接上干货! 环境: windows10系统(64位) 最好先安装jre或jdk(此软件用来打开oracle自带的可视化操作界面,不装也没关系:可以安装plsql,或者直接用命令行操作) Or ...
- 全网最详细的Windows系统里Oracle 11g R2 Client客户端(64bit)安装后的初步使用(图文详解)
不多说,直接上干货! 前期博客 全网最详细的Windows系统里Oracle 11g R2 Client(64bit)的下载与安装(图文详解) 命令行方式测试安装是否成功 1) 打开服务(cmd— ...
- 全网最详细的Windows系统里Oracle 11g R2 Database(64bit)安装后的初步使用(图文详解)
不多说,直接上干货! 前期博客 全网最详细的Windows系统里Oracle 11g R2 Database(64bit)的下载与安装(图文详解) 命令行方式测试安装是否成功 1) 打开服务(cm ...
- 全网最详细的Windows系统里Oracle 11g R2 Database(64bit)的完全卸载(图文详解)
不多说,直接上干货! 前期博客 全网最详细的Windows系统里Oracle 11g R2 Database(64bit)的下载与安装(图文详解) 若你不想用了,则可安全卸载. 完全卸载Oracle ...
随机推荐
- java代码导出数据到Excel、js导出数据到Excel(三)
jsp内容忽略,仅写个出发按钮: <button style="width: 100px" onclick="expertExcel()&quo ...
- python-函数的对象、函数嵌套、名称空间和作用域
目录 函数的对象 函数对象的四大功能 引用 当做参数传给一个函数 可以当做函数的返回值 可以当做容器类型的元素 函数的嵌套 函数的嵌套定义 函数的嵌套调用 名称空间与作用域 名称空间 内置名称空间 全 ...
- python numpy复制array
numpy快速复制array 前段时间想到一个算法,需要实现array的自我复制,直接上代码,两种复制方式, 整体复制 a=[[10,10,50,50],[10,10,40,50]] np.tile( ...
- 虚拟化技术xen,kvm,qemu区别
虚拟化类型 全虚拟化(Full Virtualization) 全虚拟化也成为原始虚拟化技术,该模型使用虚拟机协调guest操作系统和原始硬件,VMM在guest操作系统和裸硬件之间用于工作协调,一些 ...
- CodeForces - 948C Producing Snow(优先队列)
题意: n天. 每天你会堆一堆雪,体积为 v[i].每天都有一个温度 t[i] 所有之前堆过的雪在第 i 天体积都会减少 t[i] . 输出每天融化了的雪的体积. 这个题的正解我怎么想都很难理解,但是 ...
- selenium2中TestNG相关解释
testNg官网:http://testng.org/doc/documentation-main.html 新建testNG class的时候,同时也新建了一个TestNG.xml的文件. 此xml ...
- 网络编程基础socket 重要中:TCP/UDP/七层协议
计算机网络的发展及基础网络概念 问题:网络到底是什么?计算机之间是如何通信的? 早期 : 联机 以太网 : 局域网与交换机 广播 主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无 ...
- Linux inode 之我见
Linux硬盘组织方式为:引导区.超级块(superblock),索引结点(inode),数据块(datablock),目录块(diredtory block).其中超级块中包含了关于该硬盘或分区上的 ...
- 大数据学习——spark笔记
变量的定义 val a: Int = 1 var b = 2 方法和函数 区别:函数可以作为参数传递给方法 方法: def test(arg: Int): Int=>Int ={ 方法体 } v ...
- Install Oracle 11G Release 2 (11.2) on Oracle Linux 7 (OEL7)
Install Oracle 11G Release 2 (11.2) on Oracle Linux 7 (OEL7) This article presents how to install Or ...