处理Express异步中间件中的错误

时间:2018-07-17 22:35:45

标签: javascript node.js express es6-promise

我有一个async中间件,因为我想在其中使用await来清理我的代码。

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

app.use(async(req, res, next) => {
    await authenticate(req);
    next();
});

app.get('/route', async(req, res) => {
    const result = await request('http://example.com');
    res.end(result);
});

app.use((err, req, res, next) => {

    console.error(err);

    res
        .status(500)
        .end('error');
})

app.listen(8080);

问题在于,当它拒绝时,它不会转到我的错误中间件,但是如果我删除了中间件中的async关键字和throw,它就会删除。

app.get('/route', (req, res, next) => {
    throw new Error('Error');
    res.end(result);
});

所以我得到UnhandledPromiseRejectionWarning而不是输入我的错误处理中间件,如何让错误冒泡并表示处理它?<​​/ p>

6 个答案:

答案 0 :(得分:7)

  

问题是当它拒绝时,不会出现我的错误   中间件,但是如果我删除async关键字并扔进去   中间件。

express当前不支持诺言,将来的express@5.x.x

版本可能会提供支持

因此,当您传递中间件函数时,express将在try/catch块内调用它。

Layer.prototype.handle_request = function handle(req, res, next) {
  var fn = this.handle;

  if (fn.length > 3) {
    // not a standard request handler
    return next();
  }

  try {
    fn(req, res, next);
  } catch (err) {
    next(err);
  }
};

问题在于try/catch不会在Promise函数之外捕获async拒绝,并且由于express不会向其中添加.catch处理程序中间件返回的Promise,您将得到一个UnhandledPromiseRejectionWarning


最简单的方法是在中间件中添加try/catch,然后调用next(err)

app.get('/route', async(req, res, next) => {
    try {
        const result = await request('http://example.com');
        res.end(result);
    } catch(err) {
        next(err);
    }
});

但是,如果您有很多async中间件,则可能有点重复。

由于我喜欢中间件尽可能干净,并且通常会让错误冒出来,因此我在async中间件周围使用了包装器,如果承诺被拒绝,则将调用next(err),从而达到表达错误处理程序并避免使用UnhandledPromiseRejectionWarning

const asyncHandler = fn => (req, res, next) => {
    return Promise
        .resolve(fn(req, res, next))
        .catch(next);
};

module.exports = asyncHandler;

现在您可以这样称呼它:

app.use(asyncHandler(async(req, res, next) => {
    await authenticate(req);
    next();
}));

app.get('/async', asyncHandler(async(req, res) => {
    const result = await request('http://example.com');
    res.end(result);
}));

// Any rejection will go to the error handler

还有一些可以使用的软件包

答案 1 :(得分:1)

好吧,我发现了这个-https://github.com/davidbanham/express-async-errors/,然后需要脚本,您就可以开始使用

const express = require('express');
require('express-async-errors');

答案 2 :(得分:0)

使用asyncHandler进行回答既好又有用,但是在每条路由中编写此包装仍然不舒服。我建议对其进行改进:

const asyncHandler = fn => (req, res, next) => {
    return Promise
        .resolve(fn(req, res, next))
        .catch(next)
}

const methods = [
    'get',
    'post',
    'delete'  // & etc.
]

function toAsyncRouter(router) {
    for (let key in router) {
        if (methods.includes(key)) {
            let method = router[key]
            router[key] = (path, ...callbacks) => method.call(router, path, ...callbacks.map(cb => asyncHandler(cb)))
        }
    }
    return router
}

现在我们可以这样做:

const router = toAsyncRouter(express().Router())
router.get('/', someAsyncController)

等等。

分钟前,添加了一个npm模块async-express-decorator

答案 3 :(得分:0)

您需要使用try-catch,在catch部分中,只需在next()参数中传递错误,就像这样-

async create(req, res, next) {

    try {
      const userProp = req.body;
      const user = new User(userProp)

      const response = await user.save()
      const token = await user.createJWSToken()

      res.send({response, token})

    } catch (err){
      next(err)
    }
}

并且显然将这种中间件放在了index.js文件上。

app.use((err, req, res, next) => {
  res.status(422).send({ error: err.message });
});

答案 4 :(得分:0)

您需要回调异步处理程序。如果您知道承诺的概念,那就相反了。 Callbackify内置在Node中。

import util from 'util'

app.use(util.callbackify(async (req, res) => {
  await authenticate(req);
}));

它的作用是返回一个带有第三个参数的函数,该参数将是下一个函数,并在解决承诺后调用它。如果Promise被拒绝,则会以错误为参数调用下一个函数。

答案 5 :(得分:0)

Express 5 现在处理异步承诺:

https://expressjs.com/en/guide/error-handling.html

<块引用>

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