Node.js Express - 在完成整个请求处理之前提供HTTP响应

6
我有一个Node.js Express服务器,我在那里发送响应,然后尝试使用相同的请求实例进行更多处理。
如果我在头文件发送后对res进行写操作,我会得到一个错误-但是,如果我在为相应响应发送回响应后使用req可读流会发生什么情况?
换句话说,在我完成整个请求的处理之前,如何使用Node.server发送HTTP响应?
换句话说,如果我已经发送了响应,如何“消耗”请求,而不是仅仅发送响应?这真的只是除了发送响应之外的事情吗?
现在,似乎存在一些与使用已发送响应的相应对象(流)相关的奇怪错误。。
让我用代码给出一些例子,
以下展示了我的服务器中最后3个Express中间件处理程序。有趣的旁注之一是,一旦对请求调用了一个中间件处理程序,它就不能被重新调用(看起来是这样)。因此,当响应发送时,下面的第一个处理程序被调用。然后,执行另一条路径(使用process.nextTick或setImmediate)调用next(),并调用后两个处理程序,这意味着我最终会在我的服务器上得到一个404。
app.use(function (req, res, next) {

    var r;

    var timeRequired = (Date.now() - req.request_start) + 'ms';
    console.log("Time required for request:", timeRequired);

    if (r = req.lectalTemp) {
        if (!req.lectalSent) {
            req.lectalSent = true;
            res.status(r.status).json({timeRequired: timeRequired, success: r.data});
        }
        else {
            console.log('Headers sent already, but here is the data that would have been sent:', r);
        }
    }
    else {
        next();
    }

});


// catch 404 and forward to error handler
app.use(function (req, res, next) {
    var err = new Error('404: Not Found - ' + req.method + ' ' + req.originalUrl);
    err.status = 404;
    next(err);
});

app.use(function (err, req, res, next) {

    var timeRequired = (Date.now() - req.request_start) + 'ms';

    if (app.get('env') === 'production') {
        res.status(err.status || 500).json({
            error: 'sorry the API experienced an error serving your priority request'
        });
    }
    else {
        console.error(colors.bgRed(err), '\n', err.stack);

        if (!res.headersSent && !req.lectalSent) {
            req.lectalSent = true;
            res.status(err.status || 500).json({
                error: {
                    timeRequired: timeRequired,
                    errMessage: err.message,
                    errStack: err.stack
                }
            });
        }

    }
});

2
你知道如果一个中间件处理了响应,那么你就不应该继续调用next()吗?因为如果你一直调用next(),它最终会到达你的404处理程序,认为没有处理响应。如果你需要其他中间件继续处理响应,还有其他方法可以解决,但这些其他处理程序必须确保在发送响应后不会再写入响应。你可以在请求或响应上设置一个标志来指示已经发送了响应,其他处理程序可以检查该标志。 - jfriend00
是的,基本上我已经完成了。你可以继续调用一些东西并使用req流,但我认为在发送响应后使用res流可能是不允许的。 - Alexander Mills
仅供参考,我询问的原因是因为我进行了简单的优化,其中我进行了一个非常关键的数据库调用,然后进行了一个不太关键的数据库调用。我想在关键调用后返回响应,然后在发送响应后让服务器继续处理不太关键的数据库调用,这对我来说很正常。 - Alexander Mills
是的,我猜我不知道在发送响应后对 res 流采取哪些行动是合适的,哪些不是...所有事情都是禁止的还是只有一些事情..? - Alexander Mills
1
你一旦发送了响应,就不能再向响应流中写入任何内容。其他操作都可以执行。由于响应流上的大多数方法都是关于写入或准备写入的,因此建议在发送响应后避免使用响应流。 - jfriend00
显示剩余2条评论
1个回答

1
我会用队列来解决这个问题。将低优先级数据的消息发布到队列中(例如RabbitMq或类似工具),并创建一个异步地消费队列中消息的工作线程。

1
没错,这不是一个坏的方法,我们简化流程只使用一个API服务器...而且数据库调用有关联,所以我宁愿暂时将其一起处理同一个请求,只是其中1个比另一个的优先级更高,客户端只会真正关心第一次数据库调用的结果。我的做法是在服务器上使用setImmediate()将低优先级的调用放在I/O队列的末尾,而不是使用process.nextTick,在这里使用它似乎太接近同步操作并不实用。 - Alexander Mills
2
不知道为什么这里需要一个队列。听起来更像是 OP 想要在发送响应后继续运行一些代码,这是完全可以的。 - jfriend00

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接