Error Handling

错误处理是指Express如何捕获和处理同步和异步发生的错误. Express带有默认错误处理程序,因此您无需自己编写即可开始使用.

Catching Errors

确保Express能够捕获运行路由处理程序和中间件时发生的所有错误,这一点很重要.

路由处理程序和中间件内部的同步代码中发生的错误不需要任何额外的工作. 如果同步代码引发错误,则Express将捕获并处理该错误. 例如:

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

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

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

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

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

如果getUserById抛出错误或拒绝,则将使用抛出的错误或拒绝的值来调用next . 如果未提供拒绝值,则将使用Express路由器提供的默认Error对象调用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的回调, fs.writeFile是否有错误都将调用该回调. 如果没有错误,则执行第二个处理程序,否则Express捕获并处理该错误.

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

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

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

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

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

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

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

app.get('/', [
  function (req, res, next) {
    fs.readFile('/maybe-valid-file', 'utf-8', function (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收到错误.

The default error handler

Express带有内置的错误处理程序,可处理应用程序中可能遇到的任何错误. 该默认的错误处理中间件功能添加在中间件功能堆栈的末尾.

如果将错误传递给next()并且未在自定义错误处理程序中进行处理,则它将由内置错误处理程序进行处理; 错误将与堆栈跟踪一起写入客户端. 堆栈跟踪不包括在生产环境中.

Set the environment variable NODE_ENV to production, to run the app in production mode.

写入错误时,以下信息将添加到响应中:

如果您在开始编写响应后用错误调用next() (例如,如果在将响应流传输到客户端时遇到错误),则Express默认错误处理程序将关闭连接并使请求失败.

因此,当您已将标头发送到客户端时,添加自定义错误处理程序时,必须委托默认的Express错误处理程序:

function errorHandler (err, req, res, next) {
  if (res.headersSent) {
    return next(err)
  }
  res.status(500)
  res.render('error', { error: err })
}

请注意,即使您使用代码中的错误多次调用next() ,即使存在自定义错误处理中间件,也会触发默认错误处理程序.

Writing error handlers

定义错误处理中间件功能的方式与其他中间件功能相同,只是错误处理功能具有四个参数而不是三个参数: (err, req, res, next) . 例如:

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

在其他app.use()之后,您最后定义错误处理中间件并路由调用; 例如:

var bodyParser = require('body-parser')
var methodOverride = require('method-override')

app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(function (err, req, res, next) {
  // logic
})

来自中间件功能的响应可以是任何格式,例如HTML错误页面,简单消息或JSON字符串.

为了组织(和更高级别的框架)的目的,您可以定义几个错误处理中间件功能,就像使用常规中间件功能一样. 例如,为使用XHR和不使用XHR请求定义错误处理程序:

var bodyParser = require('body-parser')
var methodOverride = require('method-override')

app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)

在此示例中,通用logErrors可能会将请求和错误信息写入stderr ,例如:

function logErrors (err, req, res, next) {
  console.error(err.stack)
  next(err)
}

同样在此示例中, clientErrorHandler定义如下: 在这种情况下,错误将明确传递给下一个错误.

请注意,在错误处理函数中调用" next"时,您负责编写(并结束)响应. 否则,这些请求将"挂起",并且不符合垃圾回收的条件.

function clientErrorHandler (err, req, res, next) {
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' })
  } else {
    next(err)
  }
}

如下实现" catch-all" errorHandler函数(例如):

function errorHandler (err, req, res, next) {
  res.status(500)
  res.render('error', { error: err })
}

如果您的路由处理程序具有多个回调函数,则可以使用route参数跳到下一个路由处理程序. 例如:

app.get('/a_route_behind_paywall',
  function checkIfPaidSubscriber (req, res, next) {
    if (!req.user.hasPaid) {
      // continue handling this request
      next('route')
    } else {
      next()
    }
  }, function getPaidContent (req, res, next) {
    PaidContent.find(function (err, doc) {
      if (err) return next(err)
      res.json(doc)
    })
  })

在此示例中,将跳过getPaidContent处理程序,但app/a_route_behind_paywall所有其余处理app将继续执行.

调用next()next(err)表示当前处理程序已完成并且处于什么状态. next(err)将跳过该链中所有剩余的处理程序,除了如上所述设置为处理错误的那些处理程序.

by  ICOPY.SITE