ExpressJS Promise和EventEmitter之间的明显竞争条件

时间:2015-10-08 20:59:55

标签: javascript node.js express promise busboy

我有一个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?

1 个答案:

答案 0 :(得分: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。