# 使用中间件

Express 是一个路由和中间件 Web 框架,其自身功能最少:Express 应用程序本质上是一系列中间件函数调用。

中间件函数是可以访问应用程序请求-响应周期中的 请求对象 (req)、响应对象 (res) 和下一个中间件函数的函数。下一个中间件函数通常由一个名为 next 的变量表示。

中间件函数可以执行以下任务:

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

如果当前中间件函数没有结束请求-响应循环,它必须调用 next() 将控制权传递给下一个中间件函数。否则,请求将被挂起。

Express 应用程序可以使用以下类型的中间件:

  • 应用级中间件
  • 路由级中间件
  • 错误处理中间件
  • 内置中间件
  • 第三方中间件

您可以使用可选的挂载路径加载应用程序级和路由级中间件。您还可以将一系列中间件函数一起加载,从而在挂载点创建中间件系统的子堆栈。

# 应用级中间件

使用 app.use()app.METHOD() 函数将应用级中间件绑定到 app 对象 的实例,其中 METHOD 是中间件函数处理的请求的 HTTP 方法(如 GET、PUT 或 POST),小写。

此示例显示了一个没有挂载路径的中间件函数。每次应用收到请求时都会执行该函数。

const express = require('express')
const app = express()

app.use((req, res, next) => {
  console.log('Time:', Date.now())
  next()
})

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

app.use('/user/:id', (req, res, next) => {
  console.log('Request Type:', req.method)
  next()
})

这个例子展示了一个路由和它的处理函数(中间件系统)。该函数处理对 /user/:id 路径的 GET 请求。

app.get('/user/:id', (req, res, next) => {
  res.send('USER')
})

这是一个在挂载点加载一系列中间件函数的示例,带有挂载路径。它说明了一个中间件子堆栈,它将任何类型的 HTTP 请求的请求信息打印到 /user/:id 路径。

app.use('/user/:id', (req, res, next) => {
  console.log('Request URL:', req.originalUrl)
  next()
}, (req, res, next) => {
  console.log('Request Type:', req.method)
  next()
})

路由处理程序使您能够为路径定义多个路由。下面的示例定义了两条到 /user/:id 路径的 GET 请求路由。第二个路由不会引起任何问题,但它永远不会被调用,因为第一个路由结束了请求-响应周期。

此示例显示了一个处理对 /user/:id 路径的 GET 请求的中间件子堆栈。

app.get('/user/:id', (req, res, next) => {
  console.log('ID:', req.params.id)
  next()
}, (req, res, next) => {
  res.send('User Info')
})

// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', (req, res, next) => {
  res.send(req.params.id)
})

要跳过路由中间件堆栈中的其余中间件函数,请调用 next('route') 将控制权传递给下一个路由。注意next('route') 仅适用于使用 app.METHOD()router.METHOD() 函数加载的中间件函数。

此示例显示了一个处理对 /user/:id 路径的 GET 请求的中间件子堆栈。

app.get('/user/:id', (req, res, next) => {
  // if the user ID is 0, skip to the next route
  if (req.params.id === '0') next('route')
  // otherwise pass the control to the next middleware function in this stack
  else next()
}, (req, res, next) => {
  // send a regular response
  res.send('regular')
})

// handler for the /user/:id path, which sends a special response
app.get('/user/:id', (req, res, next) => {
  res.send('special')
})

中间件也可以在数组中声明以实现可重用性。

此示例显示了一个带有中间件子堆栈的数组,用于处理对 /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()
}

const logStuff = [logOriginalUrl, logMethod]
app.get('/user/:id', logStuff, (req, res, next) => {
  res.send('User Info')
})

# 路由级中间件

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

const router = express.Router()

使用 router.use()router.METHOD() 函数加载路由级中间件。

以下示例代码通过使用路由级中间件复制了上面显示的应用程序级中间件的中间件系统:

const express = require('express')
const app = express()
const router = express.Router()

// a middleware function with no mount path. This code is executed for every request to the router
router.use((req, res, next) => {
  console.log('Time:', Date.now())
  next()
})

// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', (req, res, next) => {
  console.log('Request URL:', req.originalUrl)
  next()
}, (req, res, next) => {
  console.log('Request Type:', req.method)
  next()
})

// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', (req, res, next) => {
  // if the user ID is 0, skip to the next router
  if (req.params.id === '0') next('route')
  // otherwise pass control to the next middleware function in this stack
  else next()
}, (req, res, next) => {
  // render a regular page
  res.render('regular')
})

// handler for the /user/:id path, which renders a special page
router.get('/user/:id', (req, res, next) => {
  console.log(req.params.id)
  res.render('special')
})

// mount the router on the app
app.use('/', router)

要跳过路由的其余中间件函数,请调用 next('router') 将控制权从路由实例传回。

此示例显示了一个处理对 /user/:id 路径的 GET 请求的中间件子堆栈。

const express = require('express')
const app = express()
const router = express.Router()

// predicate the router with a check and bail out when needed
router.use((req, res, next) => {
  if (!req.headers['x-auth']) return next('router')
  next()
})

router.get('/user/:id', (req, res) => {
  res.send('hello, user!')
})

// use the router and 401 anything falling through
app.use('/admin', router, (req, res) => {
  res.sendStatus(401)
})

# 错误处理中间件

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

以与其他中间件函数相同的方式定义错误处理中间件函数,除了使用四个参数而不是三个参数,特别是使用签名 (err, req, res, next)):

app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

有关错误处理中间件的详细信息,请参见:错误处理

# 内置中间件

从版本 4.x 开始,Express 不再依赖 Connect。以前包含在 Express 中的中间件函数现在位于单独的模块中;见 中间件函数列表

Express 具有以下内置中间件函数:

  • express.static 提供静态资源,例如 HTML 文件、图像等。
  • express.json 使用 JSON 有效负载解析传入请求。注意:可用于 Express 4.16.0+
  • express.urlencoded 使用 URL 编码的负载解析传入的请求。注意:可用于 Express 4.16.0+

# 第三方中间件

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

安装所需功能的 Node.js 模块,然后在应用程序级别或路由级别将其加载到您的应用程序中。

以下示例说明了安装和加载 cookie 解析中间件函数 cookie-parser

$ npm install cookie-parser
const express = require('express')
const app = express()
const cookieParser = require('cookie-parser')

// load the cookie-parsing middleware
app.use(cookieParser())

有关 Express 常用的第三方中间件函数的部分列表,请参阅:第三方中间件

Last Updated: 6/17/2023, 6:57:19 PM