【问题标题】:ExpressJS apparent race condition between Promise and EventEmitterPromise 和 EventEmitter 之间的 ExpressJS 明显竞争条件
【发布时间】:2016-01-06 16:05:19
【问题描述】:

我有一个 NodeJS/Express Web 应用程序,它允许用户上传一个文件,然后我使用 connect-busboy 解析它并使用 Sequelize 保存到我的数据库中。完成后,我想将用户重定向到给定页面。但是在我的 Promise 解决之前,Express 会返回 404 状态,即使我从未调用过 next(),我认为这是强制性的,以便调用中间件链中的下一个处理程序并因此导致在 404 中。

这是我目前的代码:

function uploadFormFile(req, res, next) {
   var documentInstanceID = req.params.documentInstanceID;
   // set up an object to hold my data
   var data = {
    file: null,
    documentDate: null,
    mimeType: null
   };
   // call the busboy middleware explicitly 
   // EDIT: this turned out to be the problem... of course this calls next()
   // removing this line and moving it to an app.use() made everything work as expected
   busboy(req, res, next);
   req.pipe(req.busboy);
   req.busboy.on('file', function (fieldName, file, fileName, encoding, mimeType) {
    var fileData = [];
    data.mimeType = mimeType;
    file.on('data', function (chunk) {
        fileData.push(chunk);
    });
    file.on('end', function () {
        data.file = Buffer.concat(fileData);
    });
   });
   req.busboy.on('finish', function () {
    // api methods return promises from Sequelize
    api.querySingle('DocumentInstance', ['Definition'], null, { DocumentInstanceID: documentInstanceID })
        .then(function (documentInstance) {
        documentInstance.RawFileData = data.file;
        documentInstance.FileMimeType = data.mimeType;
        // chaining promise
        return api.save(documentInstance);
       }).then(function () {
        res.redirect('/app/page');
       });
   });
}

我可以确认我的数据已正确保存。但是由于竞态条件,网页显示“无法发布”,因为 Express 返回了 404 状态,并且 res.redirect 失败并出现错误设置标头,因为它在 404 之后尝试重定向已发送。

谁能帮我弄清楚为什么 Express 会返回 404?

【问题讨论】:

  • busboy(req, res, next) 在我看来很可疑。肯定是在执行next,或者用 res 做某事,对吧?
  • 我和@KevinB 在一起——那是我一开始注意到的事情。如果您只希望 busboy 用于这种特殊情况,您可以在您的 uploadFormFile 处理程序之前添加它,例如:router.post('upload', busboy,uploadFormFile) 设置您的处理程序时。请注意,我没有使用过 Busboy,所以也许应该以不同的方式使用它,但是您所拥有的当然意味着您将在事件处理程序运行之前调用 next
  • 是的,做到了。根据@barry-johnson 的建议,我删除了对 busboy 中间件的显式调用,并将其移至路由的 app.use 中,一切都按预期开始工作。请将您的评论转化为答案,以便我可以在到期时给予信用。

标签: javascript node.js express promise busboy


【解决方案1】:

问题出在处理程序内部对 busboy 的内部调用。它不是执行并将控制权简单地返回给您的处理程序,而是调用next,在它返回控制权之前传递给它。所以你在 busboy 调用确实执行之后编码,但是请求已经超过了那个点。

如果您希望某些中间件仅针对某些请求执行,您可以将中间件链接到这些请求中,例如:

router.post('/upload',busboy,uploadFromFile)

你也可以用.use()分隔它们,例如:

router.use('/upload', busboy);
router.post('/upload', uploadFromFile);

以上任何一项都会按照您的预期链接中间件。在 .use() 的情况下,中间件也将应用于任何适用的 .METHOD(),正如 Express 在其文档中所引用的那样。

另外,请注意,您可以通过这种方式传入任意数量的中间件,既可以作为单独的参数,也可以作为中间件函数的数组,例如:

router.post('/example', preflightCheck, logSomeStuff, theMainHandler);
// or
router.post('example', [ preflightCheck,logSomeStuff ], theMainHandler);

上述任何一个示例的执行行为都是等效的。仅代表我自己,并不建议这是最佳实践,如果我在运行时构建中间件列表,我通常只使用基于数组的中间件添加。

祝你好运。希望您和我一样喜欢使用 Express。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-02
    • 2013-11-14
    • 1970-01-01
    • 2016-11-16
    • 1970-01-01
    • 2015-12-04
    • 2012-06-24
    • 1970-01-01
    相关资源
    最近更新 更多