中间件(Middleware):特指业务流程的中间处理环节。本质上就是一个function处理函数
Express中间件的调用流程:
当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
注意:中间件函数的形参列表中,必须包含next参数,路由处理函数中只包含req和res.
next函数的作用:是实现多个中间件连续调用的关键,他表示把流转关系转交给下一个中间件或路由。
定义中间件函数:
//常量midd所指向的,就是一个中间件函数
const midd = function(req,res,next) {
console.log('这是一个最简单的中间件函数')
//当前中间件的业务处理完毕后,必须调用next()函数
//表示把流转关系转交给下一个中间件或路由
next()
}
app.use(midd) // 注册为全局生效的中间件
// 简化形式
app.use(function(req,res,next) {
})
全局生效的中间件:
客户端发起的任何请求,到达服务器之后,就会触发的中间件,叫做全局生效的中间件。通过调用app.use(中间件函数),即可定义一个全局生效的中间件。
中间件的作用:
多个中间件之间,共享同一份req和res,基于这样的特性,我们可以在上游的中间件中,统一为req或res对象添加自定义的属性或方法,共下游的中间件或路由进行使用。
定义多个全局中间件:
可以使用app.use()连续定义多个全局中间件,执行时,会按定义的先后顺序依次进行调用。
局部生效的中间件:
不使用app.use()定义的中间件,叫做局部生效的中间件,
//定义中间件函数m
const m = function(req,res,next) {
console.log('这是中间件函数')
next()
}
// m这个中间件只在‘当前路由中生效’,这种用法属于‘局部生效的中间件’
app.get('/',m,function(req,res) {
res.send('Home page.')
})
// m这个中间件不会影响下面这个路由
app.get('/user',function(req,res) {
res.send('user page.')
})
// 定义多个局部中间件以及使用(等价的,根据自己需要调用,也可写成数组形式)
app.get('/',m,d,(req,res) =>{
res.send('nihao')
})
中间件的5个使用注意事项:
(1)一定要在路由之前注册中间件;
(2)客户端发送过来的请求,可以连续调用多个中间件进行处理;
(3)执行完中间件的业务代码之后,不要忘记调用next()函数;
(4)为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码;
(5)连续调用多个中间件时,多个中间件之间,共享req和res对象。
中间件的分类
为了方便大家理解和记忆中间件的使用,Express官方把常见的中间件用法,分成了5大类,分别是:
(1)应用级别的中间件;
(2)路由级别的中间件;
(3)错误级别的中间件;
(4)Express内置的中间件;
(5)第三方的中间件。
应用级别的中间件:通过app.use()或app.get(),绑定到app实例上的中间件;
路由级别的中间件:绑定到express.Router()实例上的中间件;
**错误级别的中间件:**专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
// 路由
app.get('/',function(req,res) {
throw new Error('服务器内部发生错误') // 抛出一个自定义的错误
res.send('home page.')
})
// 错误级别的中间件的function处理函数中,必须有四个形参
app.use(function(err,req,res,next) {
console.log('发生了错误:'+err.message) // 在服务器上打印错误信息
res.send('Error'+ err.message)
})
注意:必须写在所有中间件的最后面
Express内置的中间件:3个常用的中间件
(1)express.static快速托管静态资源的内置中间件。例如:HTML文件、图片、CSS样式等(无兼容性);
(2)express.json:解析JSON格式的请求体数据(由兼容性);app.use(express.json())
(3)express.urlencodeed:解析URL-encoded格式的请求体数据(有兼容性)。app.use(express.urlencodeed({extended:false}))
第三方中间件: 非官方内置,由第三方开发出来的中间件,按需下载并配置,配置步骤:
(1)首先运行npm install 中间件名(例如body-parser)安装中间件;
(2)然后使用require()导入中间件;
(3)最后用app.use()注册并使用中间件;
自定义中间件
1.需求描述与实现步骤
自己手动模拟一个类似于express.urlencoded这样的中间件,来解析 POST提交到服务器的表单数据。
实现步骤;
(1)定义中间件;
(2)监听req的data事件;
在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据。
如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。所以data事件可能会触发多次,每一次触发data事件时,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。
(3)监听req的end 事件;
当请求体数据接收完毕之后,会自动触发req的end事件。因此,可以在req的end事件中,拿到并处理完整的请求体数据。
(4)使用querystring模块解析请求体数据;
Node.js内置了一个querystring模块,专门用来处理查询字符串。通过这个模块提供的parse()函数,可以轻松把查询字符串,解析成对象的格式。
(5)将解析出来的数据对象挂载为req.body;
上游的中间件和下游的中间件及路由之间,共享同一份req和res。因此,我们可以将解析出来的数据,挂载为req的自定义属性,命名为req.body,供下游使用。
(6)将自定义中间件封装为模块。
const express = require('express')
const app = express()
// 导入自己封装的中间件模块
const customBodyParser = require('./body-parser')
// 将自定义的中间件函数,注册为全局可用的中间件
app.use(customBodyParser)
app.post('/user',(req,res) => {
res.send(req.body)
})
app.listen(80,function() {
console.log('express server running at http://127.0.0.1')
body-parser.js
// 模块封装
const qs = require('querystring')
// 定义中间件 解析表单数据的中间件
const bodyParser = 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)
// T000:把字符串格式的请求体数据,解析成对象格式
// 4.使用querystring模块解析请求体数据
const body = qs.parse(str)
// 5.将解析出来的数据对象挂载为req.body
req.body = body
next()
})
})
module.exports = bodyParser