1.中间件介绍

1.1 概念

中间件: 特指业务流程的中间处理环节。

1.2Express 中间件的调用流程

当一个请求到达Express的服务器之后, 可以连续调用多个中简介。
Express的中间件,本质上就是一个function处理函数,格式如下:

  1. app.get('/user' functionreq, res, next){
  2. next()
  3. })

注意中间件函数的形参列表中, 必须包含next参数, 而路由参数中只能包含reqres.

1.3 next() 函数的作用

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

2. 中间件初体验

注意:
中间件必须放在路由前面, 先执行中间件然后在走路由流程。
在当前中间件的业务流程处理完毕后, 必须调用next()函数, 表示把流转关系转交给下一个中间件或路由。

2.1 全局生效的中间件

客户端发起的任何请求, 到达服务器之后, 都会触发的中间件,叫做全局生效中间件
通过调用app.use(中间件函数), 即可定义一个全局生效中间件

  1. const express = require('express');
  2. const app = express();
  3. const mw = (req, res, next) => {
  4. console.log('我是第一个中间件')
  5. next()
  6. }
  7. app.use(mw)
  8. app.get('/user', (req, res) => {
  9. res.send('haha')
  10. })
  11. app.listen(80, () => {
  12. console.log('服务正常运行')
  13. })

2.2 中间件的作用

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

  1. const express = require('express');
  2. const app = express();
  3. app.use((req, res, next) => {
  4. console.log('我是第一个中间件')
  5. const date = new Date()
  6. req.date = date
  7. next()
  8. })
  9. app.get('/user', (req, res) => {
  10. res.send(req.date)
  11. })
  12. app.get('/info', (req, res) => {
  13. res.send(req.date)
  14. })
  15. app.listen(80, () => {
  16. console.log('服务正常运行')
  17. })

2.3 定义多个全局中间件

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

  1. const express = require('express');
  2. const app = express();
  3. app.use((req, res, next) => {
  4. console.log('我是第一个中间件')
  5. next()
  6. })
  7. app.use((req, res, next) => {
  8. console.log('我是第二个中间件')
  9. next()
  10. })
  11. app.get('/user', (req, res) => {
  12. res.send(req.date)
  13. })
  14. app.get('/info', (req, res) => {
  15. res.send(req.date)
  16. })
  17. app.listen(80, () => {
  18. console.log('服务正常运行')
  19. })

2.4 局部生效的中间件

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

  1. const express = require('express');
  2. const app = express();
  3. const mw1 = (req, res, next) => {
  4. console.log('我是第一个中间件')
  5. next()
  6. }
  7. const mw2 = (req, res, next) => {
  8. console.log('我是第二个中间件')
  9. next()
  10. }
  11. app.get('/user',mw1, (req, res) => {
  12. res.send(req.date)
  13. })
  14. app.get('/info',mw2, (req, res) => {
  15. res.send(req.date)
  16. })
  17. app.listen(80, () => {
  18. console.log('服务正常运行')
  19. })

2.5定义多个中间件

  1. app.get('/user',[mw1, mw2], (req, res) => {
  2. res.send(req.date)
  3. })
  4. app.get('/user', mw1, mw2, (req, res) => {
  5. res.send(req.date)
  6. })

2.6 使用中间件的注意事项

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

    3. 中间件的分类

  • 应用级别的中间件

  • 路由级别的中间件
  • 错误级别的中间件
  • Express内置的中间件
  • 第三方的中间件

    3.1 应用级别的中间件

    通过app.use()app.get()或者app.post(),绑定到实例上的中间件,叫做应用级别的中间件,
    1. app.use((req, res, next) => {
    2. console.log('我是第一个中间件')
    3. next()
    4. })
    5. app.get('/user', mw1, mw2, (req, res) => {
    6. res.send(req.date)
    7. })

    3.2 路由级别的中间件

    绑定到express.Router()实例上的中间件, 叫做路由级别的中间件。 它的用法和应用级别中间件没有任何区别。 只不过, 应用级别中间件是绑定到app实例上的, 路由级别中间件绑定到router实例上。
    1. const express = require('express');
    2. const app = express();
    3. const router = express.Router()
    4. router.use( (req, res, next) => {
    5. console.log('路由级别中间件')
    6. req.desc= '我是一个路由级别的中间件'
    7. next()
    8. })
    9. app.use('/user', router)
    10. app.get('/user', (req, res) => {
    11. res.send(req.desc)
    12. })
    13. app.listen(80, () => {
    14. console.log('服务正常运行')
    15. })

    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’) })
  1. <a name="QQz51"></a>
  2. ### 3.4.2 express.json
  3. 解析JSON格式的请求数据(有兼容性)
  4. ```javascript
  5. const express = require('express');
  6. const app = express();
  7. //通过express.json这个中间件,间隙表单中的json格式的数据
  8. console.log(app.use(express.json()))
  9. app.post('/add',(req, res) => {
  10. console.log(req.body)
  11. //{ name: 'lisa', age: 20 }
  12. /**
  13. * 在服务器中, 可以使用req.body这个属性 来接收客户端发送过来的请求体数据
  14. * 默认情况下, 如果不配置解析表单数据的中间件,则req.body默认等于undefined
  15. */
  16. res.send('OK') } ) app.listen(80, () => {
  17. console.log('server is runnning at 127.0.0.1:80')
  18. })

3.4.3 express.urlencoded

  • 解析URL-encoded格式的请求体数据(有兼容性)
  • 安装中间件:npm install body-parser
  • 导入中间件:const parser = require(‘body-parser’)
  • 调用中间件:app.use(parser.urlencoded({extended: false}))
    1. const express = require('express');
    2. //导入解析表单数据的中间件 body-parse
    3. const parser = require('body-parser')
    4. const app = express();
    5. //使用app.use注册中间件
    6. app.use(parser.urlencoded({extended: false}))
    7. //通过express.json这个中间件,间隙表单中的json格式的数据
    8. // console.log(app.use(express.urlincoded()))
    9. app.post('/add',(req, res) => {
    10. console.log(req.body)
    11. //{ name: 'Iric', address: '陕西省' }
    12. /**
    13. * 在服务器中, 可以使用req.body这个属性 来接收客户端发送过来的请求体数据
    14. * 默认情况下, 如果不配置解析表单数据的中间件,则req.body默认等于undefined
    15. */
    16. res.send('OK') } ) app.listen(80, () => {
    17. console.log('server is runnning at 127.0.0.1:80')
    18. })
    注意: Express内置的express.urlencoded中间件, 就是基于body-parser这个第三方中间件进一步封装出来的。
    所以也可以直接使用express.urlencoded解析地址
    1. const express = require('express');
    2. const app = express();
    3. app.use(express.urlencoded({extended: false}))
    4. app.post('/add',(req, res) => {
    5. console.log(req.body)
    6. //{ name: 'Iric', address: '陕西省', age: '16' }
    7. /**
    8. * 在服务器中, 可以使用req.body这个属性 来接收客户端发送过来的请求体数据
    9. * 默认情况下, 如果不配置解析表单数据的中间件,则req.body默认等于undefined
    10. */
    11. res.send('OK') } ) app.listen(80, () => {
    12. console.log('server is runnning at 127.0.0.1:80')
    13. })

    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 将自定义中间件封装为模块

    1. const express = require('express')
    2. const qs = require('querystring')
    3. const app = express()
    4. //解析表单数据的中间件
    5. app.use((req, res, next) => {
    6. //定义一个str字符串, 专门用来存储客户端发送过来的请求体数据
    7. let str = ''
    8. //监听req对象的data事件(客户端发送过来的新的请求数据)
    9. req.on('data', (chunk) => {
    10. //拼接请求体数据, 隐式转换为字符串
    11. str += chunk;
    12. })
    13. //监听req的end事件
    14. req.on('end', () => {
    15. //把字符串格式的请求体数据, 解析成对象格式
    16. const body = qs.parse(str)
    17. req.body = body;
    18. next()
    19. })
    20. })
    21. app.post('/add', (req, res) => {
    22. res.send(req.body)
    23. })
    24. app.listen(80, () => {
    25. console.log('server is running')
    26. })