捕捉错误

中英双语

确保 Express 捕获运行路由处理程序和中间件时发生的所有错误非常重要。

路由处理程序和中间件内的同步代码中发生的错误不需要额外的工作。如果同步代码抛出错误,Express 将捕获并处理它。例如:

app.get('/', (req, res) => {
  throw new Error('BROKEN') // Express will catch this on its own.
})

对于路由处理程序和中间件调用的异步函数返回的错误,您必须将它们传递给 next() 函数,Express 将在其中捕获并处理它们。例如:

app.get('/', (req, res, next) => {
  fs.readFile('/file-does-not-exist', (err, data) => {
    if (err) {
      next(err) // Pass errors to Express.
    } else {
      res.send(data)
    }
  })
})

从 Express 5 开始,返回 Promise 的路由处理程序和中间件将在拒绝或抛出错误时自动调用 next(value)。例如:

app.get('/user/:id', async (req, res, next) => {
  const user = await getUserById(req.params.id)
  res.send(user)
})

如果 getUserById 抛出错误或拒绝,next 将使用抛出的错误或拒绝的值调用。如果没有提供拒绝值,则将使用 Express 路由器提供的默认错误对象调用 next

如果您将任何内容传递给 next() 函数(字符串 'route' 除外),Express 会将当前请求视为错误,并将跳过任何剩余的非错误处理路由和中间件函数。

如果序列中的回调不提供数据,只提供错误,您可以简化此代码,如下所示:

app.get('/', [
  function (req, res, next) {
    fs.writeFile('/inaccessible-path', 'data', next)
  },
  function (req, res) {
    res.send('OK')
  }
])

在上面的例子中,next 是作为 fs.writeFile 的回调提供的,无论是否有错误都会调用它。如果没有错误,则执行第二个处理程序,否则 Express 会捕获并处理错误。

您必须捕获路由处理程序或中间件调用的异步代码中发生的错误,并将它们传递给 Express 进行处理。例如:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('BROKEN')
    } catch (err) {
      next(err)
    }
  }, 100)
})

上面的示例使用 try...catch 块来捕获异步代码中的错误并将它们传递给 Express。如果 try...catch 块被省略,Express 将不会捕获错误,因为它不是同步处理程序代码的一部分。

使用 Promise 可以避免 try...catch 块的开销,或者在使用返回 Promise 的函数时。例如:

app.get('/', (req, res, next) => {
  Promise.resolve().then(() => {
    throw new Error('BROKEN')
  }).catch(next) // Errors will be passed to Express.
})

由于 Promise 自动捕获同步错误和被拒绝的 Promise,您可以简单地提供 next 作为最终的 catch 处理程序,Express 将捕获错误,因为 catch 处理程序将错误作为第一个参数。

您还可以使用一系列处理程序来依赖同步错误捕获,方法是将异步代码减少到微不足道的程度。例如:

app.get('/', [
  function (req, res, next) {
    fs.readFile('/maybe-valid-file', 'utf-8', (err, data) => {
      res.locals.data = data
      next(err)
    })
  },
  function (req, res) {
    res.locals.data = res.locals.data.split(',')[1]
    res.send(res.locals.data)
  }
])

上面的例子有几个来自 readFile 调用的琐碎语句。如果 readFile 导致错误,则将错误传递给 Express,否则您将在链中的下一个处理程序中快速返回同步错误处理的世界。然后,上面的示例尝试处理数据。如果失败,则同步错误处理程序将捕获它。如果你在 readFile 回调中完成了这个处理,那么应用程序可能会退出并且 Express 错误处理程序不会运行。

无论您使用哪种方法,如果您希望 Express 错误处理程序被调用并且应用程序能够继续存在,您必须确保 Express 接收到错误。