1.中间件介绍
1.1 概念
1.2Express 中间件的调用流程
当一个请求到达Express的服务器之后, 可以连续调用多个中简介。
Express的中间件,本质上就是一个function处理函数,格式如下:
app.get('/user', function(req, res, next){next()})
注意中间件函数的形参列表中, 必须包含next参数, 而路由参数中只能包含req和res.
1.3 next() 函数的作用
next函数是实现多个中间件连续调用的关键, 它表示把流转关系转交给下一个中间件或路由。
2. 中间件初体验
注意:
中间件必须放在路由前面, 先执行中间件然后在走路由流程。
在当前中间件的业务流程处理完毕后, 必须调用next()函数, 表示把流转关系转交给下一个中间件或路由。
2.1 全局生效的中间件
客户端发起的任何请求, 到达服务器之后, 都会触发的中间件,叫做全局生效的中间件。
通过调用app.use(中间件函数), 即可定义一个全局生效的中间件,
const express = require('express');const app = express();const mw = (req, res, next) => {console.log('我是第一个中间件')next()}app.use(mw)app.get('/user', (req, res) => {res.send('haha')})app.listen(80, () => {console.log('服务正常运行')})
2.2 中间件的作用
多个中间件之间, 共享一份req和res.基于这样的特性, 我们可以在上游的中间件中统一为req或者res对象添加自定义属性或者方法, 供下游的中间件使用或者路由使用。
const express = require('express');const app = express();app.use((req, res, next) => {console.log('我是第一个中间件')const date = new Date()req.date = datenext()})app.get('/user', (req, res) => {res.send(req.date)})app.get('/info', (req, res) => {res.send(req.date)})app.listen(80, () => {console.log('服务正常运行')})
2.3 定义多个全局中间件
可以使用app.use()连续定义多个全局中间件。客户端请求到服务器之后, 会按照中间件定义的先后顺序依次进行调用, 实例代码如下:
const express = require('express');const app = express();app.use((req, res, next) => {console.log('我是第一个中间件')next()})app.use((req, res, next) => {console.log('我是第二个中间件')next()})app.get('/user', (req, res) => {res.send(req.date)})app.get('/info', (req, res) => {res.send(req.date)})app.listen(80, () => {console.log('服务正常运行')})
2.4 局部生效的中间件
不使用app.use()定义的中间件,叫做局部生效的中间件, 实例代码如下:
const express = require('express');const app = express();const mw1 = (req, res, next) => {console.log('我是第一个中间件')next()}const mw2 = (req, res, next) => {console.log('我是第二个中间件')next()}app.get('/user',mw1, (req, res) => {res.send(req.date)})app.get('/info',mw2, (req, res) => {res.send(req.date)})app.listen(80, () => {console.log('服务正常运行')})
2.5定义多个中间件
app.get('/user',[mw1, mw2], (req, res) => {res.send(req.date)})app.get('/user', mw1, mw2, (req, res) => {res.send(req.date)})
2.6 使用中间件的注意事项
- 一定要在路由之前注册中间件
- 客户端发送过来的请求, 可以来纳许调用多个中间件进行处理
- 执行完中间件的业务代码之后, 不要忘记调用next()函数
- 为了防止代码逻辑混乱, 调用next()函数后不要再写额外的代码
连续调用多个中间件时,多个中间件之间, 共享req和res对象。
3. 中间件的分类
应用级别的中间件
- 路由级别的中间件
- 错误级别的中间件
- Express内置的中间件
- 第三方的中间件
3.1 应用级别的中间件
通过app.use()或app.get()或者app.post(),绑定到实例上的中间件,叫做应用级别的中间件,app.use((req, res, next) => {console.log('我是第一个中间件')next()})app.get('/user', mw1, mw2, (req, res) => {res.send(req.date)})
3.2 路由级别的中间件
绑定到express.Router()实例上的中间件, 叫做路由级别的中间件。 它的用法和应用级别中间件没有任何区别。 只不过, 应用级别中间件是绑定到app实例上的, 路由级别中间件绑定到router实例上。const express = require('express');const app = express();const router = express.Router()router.use( (req, res, next) => {console.log('路由级别中间件')req.desc= '我是一个路由级别的中间件'next()})app.use('/user', router)app.get('/user', (req, res) => {res.send(req.desc)})app.listen(80, () => {console.log('服务正常运行')})
3.3错误级别的中间件
错误级别的中间件作用: 专门用来捕获整个项目中发生的一场错误, 从而防止项目一场奔溃的问题。
格式: 错误级别的中间件的function处理函数中, 必须有4个形参, 形参顺序从前至后, 分别是err, req, res, next()。3.4 内置中间件
3.4.1express.static
express.static快速托管静态资源的内置中间件, 例如: HTML文件、图片、CSS样式等(无兼容性) ```javascript const express = require(‘express’); const app = express(); app.use(‘/public’,express.static(‘./src’)) app.get(‘/user’, (req, res) => { res.send(‘哈哈哈’)
}) app.listen(80, () => { console.log(‘server is runnning at 127.0.0.1:80’) })
<a name="QQz51"></a>### 3.4.2 express.json解析JSON格式的请求数据(有兼容性)```javascriptconst express = require('express');const app = express();//通过express.json这个中间件,间隙表单中的json格式的数据console.log(app.use(express.json()))app.post('/add',(req, res) => {console.log(req.body)//{ name: 'lisa', age: 20 }/*** 在服务器中, 可以使用req.body这个属性 来接收客户端发送过来的请求体数据* 默认情况下, 如果不配置解析表单数据的中间件,则req.body默认等于undefined*/res.send('OK') } ) app.listen(80, () => {console.log('server is runnning at 127.0.0.1:80')})
3.4.3 express.urlencoded
- 解析URL-encoded格式的请求体数据(有兼容性)
- 安装中间件:npm install body-parser
- 导入中间件:const parser = require(‘body-parser’)
- 调用中间件:app.use(parser.urlencoded({extended: false}))
注意: Express内置的express.urlencoded中间件, 就是基于body-parser这个第三方中间件进一步封装出来的。const express = require('express');//导入解析表单数据的中间件 body-parseconst parser = require('body-parser')const app = express();//使用app.use注册中间件app.use(parser.urlencoded({extended: false}))//通过express.json这个中间件,间隙表单中的json格式的数据// console.log(app.use(express.urlincoded()))app.post('/add',(req, res) => {console.log(req.body)//{ name: 'Iric', address: '陕西省' }/*** 在服务器中, 可以使用req.body这个属性 来接收客户端发送过来的请求体数据* 默认情况下, 如果不配置解析表单数据的中间件,则req.body默认等于undefined*/res.send('OK') } ) app.listen(80, () => {console.log('server is runnning at 127.0.0.1:80')})
所以也可以直接使用express.urlencoded解析地址const express = require('express');const app = express();app.use(express.urlencoded({extended: false}))app.post('/add',(req, res) => {console.log(req.body)//{ name: 'Iric', address: '陕西省', age: '16' }/*** 在服务器中, 可以使用req.body这个属性 来接收客户端发送过来的请求体数据* 默认情况下, 如果不配置解析表单数据的中间件,则req.body默认等于undefined*/res.send('OK') } ) app.listen(80, () => {console.log('server is runnning at 127.0.0.1:80')})
3.5 自定义中间件
3.5.1 定义中间件
使用app.use()来定义全局生效的中间件3.5.2 监听req的data事件
:::success 在中间件中, 需要监听req对象的data事件, 来获取客户端到服务器的数据。如果数据量比较大, 无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。 所以data事件可能会触发多次。 每一次触发data事件时, 获取到数据只是完整数据的一部分, 需要手动对接收到的数据进行拼接。 :::3.5.3 监听req的end事件
当请求体数据接收完毕之后, 会自动触发req的end事件。因此,我们可以在req的end事件中, 拿到并处理完整的请求体数据。3.5.4 使用querystring模块解析请求体数据
node.js内置了一个querystring模块, 专门用来处理查询字符串。通过这个模块提供的parse()函数,可以轻松把查询字符串, 解析成对象的格式。3.5.5 将解析出来的数据对象挂载为req.body
上游的中间件和下游的中间件之间,共享一份req和res.因此,我们可以将解析出来的数据, 挂载为req的自定义属性, 命名为req.body,供下游使用。3.5.6 将自定义中间件封装为模块
const express = require('express')const qs = require('querystring')const app = express()//解析表单数据的中间件app.use((req, res, next) => {//定义一个str字符串, 专门用来存储客户端发送过来的请求体数据let str = ''//监听req对象的data事件(客户端发送过来的新的请求数据)req.on('data', (chunk) => {//拼接请求体数据, 隐式转换为字符串str += chunk;})//监听req的end事件req.on('end', () => {//把字符串格式的请求体数据, 解析成对象格式const body = qs.parse(str)req.body = body;next()})})app.post('/add', (req, res) => {res.send(req.body)})app.listen(80, () => {console.log('server is running')})
