• 【Node.js】Node.js入门(六):Express中间件函数


    1、简述

    啥是中间件函数,先不百度,想象理解下:在web请求-响应的循环中,会根据请求调用回调函数,并返回响应,回调函数会有嵌套调用,把这些函数固定参数形式,就是中间件。
    以上是小白为了做心理上的假定合理性猜测,等深入了解后,再改正。也希望大神们留言指教。

    中间件函数完成以下功能:

    执行任何代码。
    对请求和响应对象进行更改。
    结束请求-响应周期。
    调用堆栈中的下一个中间件。
    
    • 1
    • 2
    • 3
    • 4

    Express中间件函数接受三个参数:请求对象(req)、响应对象 (res)、next()函数。
    如果当前中间件函数没有结束请求-响应周期,它必须调用next()将控制传递给下一个中间件函数。否则,请求将被搁置。

    2、示例:Hello World

    示例功能:每次应用程序收到一个请求,它打印消息“LOGGED”到终端。

    中间件加载的顺序很重要:首先加载的中间件函数也会首先执行。
    如果myLogger是在到根路径的路由之后加载的,那么请求永远不会到达它,应用程序也不会打印“LOGGED”,因为根路径的路由处理程序终止了请求-响应周期。
    中间件函数myLogger只是打印一条消息,然后通过调用next()函数将请求传递给堆栈中的下一个中间件函数。

    var express = require('express')
    var app = express()
    
    var myLogger = function (req, res, next) {
      console.log('LOGGED')
      next()
    }
    
    app.use(myLogger)
    
    app.get('/', function (req, res) {
      res.send('Hello World!')
    })
    
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3、示例:返回值创建、使用

    示例功能是在web页面中显示时间,和上例代码结构一样,不同点是:这个示例演示了返回值在回调函数中如何传递。下例中req.requestTime在中间件函数中创建并设置为当前时间,在回调函数中直接使用。对于C程序员来说,这种使用方式好神奇啊!

    var express = require('express')
    var app = express()
    
    var requestTime = function (req, res, next) {
      req.requestTime = Date.now()
      next()
    }
    
    app.use(requestTime)
    
    app.get('/', function (req, res) {
      var responseText = 'Hello World!
    '
    responseText += 'Requested at: ' + req.requestTime + '' res.send(responseText) }) app.listen(3000)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4、示例:错误处理

    本示例功能:创建一个中间件函数,该函数验证传入的cookie并在cookie无效时发送一个400响应。

    本示例使用cookie解析器中间件从req对象解析传入的cookie,并将它们传递给cookieValidator函数。validateCookies中间件返回一个Promise,在拒绝时将自动触发我们的错误处理程序。

    注意在cookieValidator(req.cookies)之后调用next()。这确保了在cookieValidator解析时,堆栈中的下一个中间件将被调用,即异步执行。
    如果将任何东西传递给next()函数(字符串’route’或’router’除外),Express将认为当前请求是一个错误,并将跳过所有剩余的非错误处理路由和中间件函数。(这个功能又很突然,怎么就认为是错误了,没有看到条件判断啊?疑问先留着,待以后解决)

    async function cookieValidator (cookies) {
      try {
        await externallyValidateCookie(cookies.testCookie)
      } catch {
        throw new Error('Invalid cookies')
      }
    }
    
    var express = require('express')
    var cookieParser = require('cookie-parser')
    var cookieValidator = require('./cookieValidator')
    
    var app = express()
    
    async function validateCookies (req, res, next) {
      await cookieValidator(req.cookies)
      next()
    }
    
    app.use(cookieParser())
    
    app.use(validateCookies)
    
    // 错误处理
    app.use(function (err, req, res, next) {
      res.status(400).send(err.message)
    })
    
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5、示例:使中间件可配置

    创建hello.js中间件文件,在这个文件中使用module.exports导出中间件函数,根据传入的函数参数,在中间件函数中执行不同功能

    module.exports = function (options) {
      return function (req, res, next) {
        // 在这里根据下面主程序传入的option1、option2来执行不同功能,实现中间件可配置。
        next()
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在app主程序中使用

    var mw = require('./hello.js')
    app.use(mw({ option1: '1', option2: '2' }))
    
    • 1
    • 2

    6、应用层中间件

    1)示例展示:没有指定路由,并且可以响应任何请求的中间件。

    var express = require('express')
    var app = express()
    
    app.use(function (req, res, next) {
      console.log('Time:', Date.now())
      next()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2)示例展示:一个挂载在/user/:id路径上的中间件函数。该函数对/user/:id路径上的任何类型的HTTP请求执行。

    app.use('/user/:id', function (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    })
    
    • 1
    • 2
    • 3
    • 4

    3)示例展示:在/user/:id路由上,处理get请求

    app.get('/user/:id', function (req, res, next) {
      res.send('USER')
    })
    
    • 1
    • 2
    • 3

    4)示例展示:中间件堆栈,即串联多个处理函数

    app.use('/user/:id', function (req, res, next) {
      console.log('Request URL:', req.originalUrl)
      next()
    }, function (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5)示例展示:结束请求-响应周期

    下面的示例为到/user/:id路径的GET请求定义了两条路由。第二个路由永远不会被调用,因为第一个路由最后没有调用next()结束了请求-响应周期。

    app.get('/user/:id', function (req, res, next) {
      console.log('ID:', req.params.id)
      next()
    }, function (req, res, next) {
      res.send('User Info')
    })
    
    app.get('/user/:id', function (req, res, next) {
      res.send(req.params.id)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    6)示例展示:跳过中间件堆栈中其余中间件函数

    可以调用next(‘route’)将控制传递给下一个路由。注意:next(‘route’)只在使用app.METHOD()或router.METHOD()函数加载的中间件函数中工作。

    app.get('/user/:id', function (req, res, next) {
      // 如果用户ID为0,则跳转到下一条路由
      if (req.params.id === '0') next('route')
      // 如果用户ID为0,则跳转到下一条路由
      else next()
    }, function (req, res, next) {
      res.send('regular')
    })
    
    app.get('/user/:id', function (req, res, next) {
      res.send('special')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    7)示例展示:中间件函数数组

    为了可重用性,中间件也可以声明在数组中。
    这个例子展示了一个带有中间件子堆栈的数组,该数组处理到/user/:id路径的GET请求

    function logOriginalUrl (req, res, next) {
      console.log('Request URL:', req.originalUrl)
      next()
    }
    
    function logMethod (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    }
    
    var logStuff = [logOriginalUrl, logMethod]
    app.get('/user/:id', logStuff, function (req, res, next) {
      res.send('User Info')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    7、路由层中间件

    路由层中间件与应用层中间件的工作方式相同,只是它被绑定到express.Router()的实例上。

    var router = express.Router()
    
    • 1

    通过使用router.use()和router.METHOD()函数加载路由层中间件。

    下面的示例和=6=中应用层中间件的示例基本相同:

    var express = require('express')
    var app = express()
    var router = express.Router()
    
    // 没有挂载路径的中间件函数。对于每个向路由器的请求都会执行此代码
    router.use(function (req, res, next) {
      console.log('Time:', Date.now())
      next()
    })
    
    // 中间件子堆栈会显示到/user/:id路径的任何类型的HTTP请求的请求信息
    router.use('/user/:id', function (req, res, next) {
      console.log('Request URL:', req.originalUrl)
      next()
    }, function (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    })
    
    // 处理到/user/:id路径的GET请求的中间件子堆栈
    router.get('/user/:id', function (req, res, next) {
      // 如果用户ID为0,则跳转到下一个路由器
      if (req.params.id === '0') next('route')
      // 否则,将控制传递给此堆栈中的下一个中间件函数
      else next()
    }, function (req, res, next) {
      // 渲染普通页面
      res.render('regular')
    })
    
    // user/:id路径的处理程序,它呈现一个特殊的页面
    router.get('/user/:id', function (req, res, next) {
      console.log(req.params.id)
      res.render('special')
    })
    
    // /user/:id路径的处理程序,它呈现一个特殊的页面
    app.use('/', router)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    要跳过路由器的其他中间件函数,调用next(‘router’)将控制传递回路由器实例。
    这个例子展示了一个中间件堆栈,它处理到/user/:id路径的GET请求

    var express = require('express')
    var app = express()
    var router = express.Router()
    
    // 用检查谓词路由器,并在需要时提取
    router.use(function (req, res, next) {
      if (!req.headers['x-auth']) return next('router')
      next()
    })
    
    router.get('/user/:id', function (req, res) {
      res.send('hello, user!')
    })
    
    // 执行:next('router')
    app.use('/admin', router, function (req, res) {
      res.sendStatus(401)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    8、错误处理中间件

    错误处理中间件总是接受四个参数。必须提供四个参数将其标识为错误处理中间件函数。即使不需要使用下一个对象,也必须指定它来维护签名。否则,下一个对象将被解释为常规中间件,将无法处理错误。

    app.use(function (err, req, res, next) {
      console.error(err.stack)
      res.status(500).send('Something broke!')
    })
    
    • 1
    • 2
    • 3
    • 4

    9、内置的中间件

    Express有以下内置的中间件:

    express.static:服务于静态资产,如HTML文件、图像等。
    express.json:使用内置json中间件解析传入请求。
    express.urlencoded:使用内置url编码中间件解析传入的请求
    
    • 1
    • 2
    • 3

    10、第三方中间件

    使用第三方中间件为Express应用程序添加功能。

    先安装,再加载:
    下面的示例演示了安装和加载中间件函数cookie-parser。

    $ npm install cookie-parser
    
    • 1
    var express = require('express')
    var app = express()
    var cookieParser = require('cookie-parser')
    
    // 加载cookie解析中间件
    app.use(cookieParser())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    常用第三方中间件参见:https://www.expressjs.com.cn/resources/middleware.html

  • 相关阅读:
    注册中心服务eureka 切换到 nocas遇到的问题
    Linux不用root权限安装nvcc
    AndroidStudio案例——跑马灯
    深入理解HTTP/HTTPS协议
    Python基于OpenCV&YOLO台球击球路线规划系统(源码&部署教程)
    Spring MVC
    CDN加速技术:国内外优劣势
    一文讲透为Power Automate for Desktop (PAD) 实现自定义模块 - 附完整代码
    java医药配送服务系统ssm447
    跨境erp系统功能分析
  • 原文地址:https://blog.csdn.net/u010168781/article/details/127616532