Express和async / await:我应该用try / catch包装很多东西吗?

Mer*_*erc 1 try-catch node.js express async-await

我有以下代码:

app.post('/routes/passwordReset/:userId', async (req, res, next) => {
  var userId = req.params.userId

  let record = (await vars.connection.queryP('SELECT * FROM contacts WHERE id = ?', userId))[0]

  if (!record) {
    res.status(404).send('')
    return
  }

  // Calculate token and expiry, update database
  var token = require('crypto').randomBytes(48).toString('base64').replace(/[^a-zA-Z0-9]/g, '').substr(0, 28)
  var now = new Date()
  var inOneHour = new Date(now.getTime() + (1 * 1000 * 60 * 60))
  await vars.connection.queryP('UPDATE contacts SET recoverToken = ?, recoverTokenExpiry = ? WHERE id = ?', [ token, inOneHour, userId ])
  res.status(200).send('')
})
Run Code Online (Sandbox Code Playgroud)

如果我创建了一个人为错误(例如,人为的字段recoverTokenExpiry太短),我将得到:

[[12:19:55.649]] [错误](节点:11919)UnhandledPromiseRejectionWarning:未处理的承诺拒绝(拒绝ID:4):错误:ER_DATA_TOO_LONG:第1行的列'recoverToken'的数据太长

我以某种方式认为Express可以尝试/捕获中间件,并next(err)在抛出错误时调用它。也许不是吗?

因此,如果出现错误,我应该用try / catch(e)包装每条路线,然后执行next(e)吗?

Fra*_*teo 5

Express不会为您做任何事情。这是非常裸露的骨头。

您有两种选择:

  1. 将所有内容包装在 try-catch
  2. 写你自己的错误处理中间件作为显示在文档这里

调用next()会将req和传递res给堆栈中的下一个中间件。

调用next(err),其他方式是给它提供一个Exception对象,它将调用堆栈中的所有错误中间件。


在自己的文件中定义错误处理程序:

// error-middleware.js

/**
 * Handler to catch `async` operation errors.
 * Reduces having to write `try-catch` all the time.
 */
exports.catchErrors = action => (req, res, next) => action(req, res).catch(next)

/**
 * Show useful information to client in development.
 */
exports.devErrorHandler = (err, req, res, next) => {
  err.stack = err.stack || ''
  const status = err.status || 500
  const error = { message: err.message }
  res.status(status)
  res.json({ status, error })
}
Run Code Online (Sandbox Code Playgroud)

然后,将密码重置逻辑移出app.post其专用功能:

// password-controller.js

exports.resetPassword = async (req, res) => {
  // do work
  // ...
}
Run Code Online (Sandbox Code Playgroud)

这样可以更轻松地编写针对的单元测试,并明确分离关注点。

接下来,创建您的密码重置路径:

// password-reset-routes.js

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

const passwordController = require('./password-controller')
const { catchErrors } = require('./error-middleware')

router.post('/routes/passwordReset/:userId', catchErrors(passwordController.resetPassword))

module.exports = router
Run Code Online (Sandbox Code Playgroud)

请注意,上面定义的控制器已在此处导入和使用。此外,您还会看到控制器动作resetPassword由包裹catchErrors。这样可以避免一直写try-catch,并且catchErrors中间件会捕获任何错误。

最后,将其全部连接到您的主app.js文件中:

// app.js

const express = require('express')
const { devErrorHandler } = require('./error-middleware')
const passwordResetRoutes = require('./password-reset-routes.js')
const app = express()

app.use(devErrorHandler)
app.use(passwordResetRoutes)

module.exports = app
Run Code Online (Sandbox Code Playgroud)