Express和async / await:我应该用try / catch包装好吗?

时间:2018-04-05 04:22:23

标签: node.js express async-await try-catch

我有以下代码:

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('')
})

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

  

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

我不知何故认为Express会尝试/捕获中间件并在调用错误时调用next(err)。也许不是这样?

那么,我应该用try / catch(e)包装每个路由,如果有错误,请执行next(e)吗?

3 个答案:

答案 0 :(得分:5)

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

您有两种选择:

  1. 将所有内容包裹在try-catch
  2. 编写您自己的错误处理中间件,如文档here中所示。
  3. 调用next()会将reqres传递给堆栈中的下一个中间件。

    调用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 })
    }
    

    然后将密码重置逻辑移出app.post并进入其自己的专用功能:

    // password-controller.js
    
    exports.resetPassword = async (req, res) => {
      // do work
      // ...
    }
    

    这使得编写单元测试和明确分离关注点变得更加容易。

    接下来创建密码重置路由:

    // 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
    

    请注意,上面定义的控制器是在此处导入和使用的。此外,您会看到控制器操作resetPassword包含catchErrors。这样可以避免必须始终写try-catchcatchErrors中间件会捕获任何错误。

    最后,将它连接到主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
    

答案 1 :(得分:1)

好吧,因为您正在使用await关键字,这意味着执行流程不再异步,这就是它失败的原因,是的,您每次都必须使用try-catch块。为避免这种情况,您可以改为遵循异步流程并分别使用then()和catch(),这样您就可以执行以下操作:

...
.then((status)=>{
  res.status(200).send('')
})
.catch((err)=>{
  next(err);
});
...

答案 2 :(得分:0)

正如其他人已经回复,因为您正在使用等待,您可以将承诺拒绝处理为

let record = await vars.connection.queryP('SELECT * FROM contacts WHERE id = ?', userId))[0].then(function () {
     console.log("Promise Resolved");
}).catch((error)=>{
    console.log("Promise rejected")
})