Node.js 发送后无法设置 headers 引入 res.render() 后出错

Node.js Can't set headers after they are sent Error after introducing res.render()

我有这段代码:

   app.post('/pst', function(req, res) {
            var data = req.body.convo;

            res.render('waiting.ejs');  //ADDED THIS

            myFunc(data).then(result => {


            res.render('success.ejs');  //THEN THIS

            //---------------------------------
            //clever way to send text file to client from the memory of the server
            var fileContents = Buffer.from(result, 'ascii');
            var readStream = new stream.PassThrough();
            readStream.end(fileContents);
            res.set('Content-disposition', 'attachment; filename=' + fileName);
            res.set('Content-Type', 'text/plain');
            readStream.pipe(res);
            //--------------------------------------

            }).catch( .....

我评论为'clever way to send file from memory of the server'的代码来自这个post:

它的作用是从内存中获取一个字符串并将其作为 .txt 文件提供给客户端。

此代码曾经有效。

然后我决定添加 res.render('waiting.ejs'); 行,但我得到了这个错误:

Error: Can't set headers after they are sent.

然后我尝试在将 .txt 文件发送到客户端的代码之前和之后添加另一个 res.render() [在本例中为 res.render('success.ejs');]。

错误依旧。此外,没有重定向到 success.ejs,换句话说,res.render('success.ejs'); 从未工作过,尽管它是否放在那段代码之后的 ofr 之前。

您必须检查 express.js 源代码 (here):

res.render = function render(view, options, callback) {
  var app = this.req.app;
  var done = callback;
  var opts = options || {};
  var req = this.req;
  var self = this;

  // support callback function as second arg
  if (typeof options === 'function') {
    done = options;
    opts = {};
  }

  // merge res.locals
  opts._locals = self.locals;

  // default callback to respond
  done = done || function (err, str) {
    if (err) return req.next(err);
    self.send(str);
  };

  // render
  app.render(view, opts, done);
};

你可以看到,当你使用res.render()方法时,它会将完成的回调传递给app.render(...)source code),然后它将done传递给tryInitView

最后,它将调用 done 回调,如果成功则调用 str,如果失败则调用 err。然后它会在 done 回调中触发 res.send(),这只会阻止您在那之后设置 headers。

   app.post('/pst', function(req, res) {
            var data = req.body.convo;

            myFunc(data).then(result => {


          

            //---------------------------------
            //clever way to send text file to client from the memory of the server
            var fileContents = Buffer.from(result, 'ascii');
            var readStream = new stream.PassThrough();
            readStream.end(fileContents);
            res.set('Content-disposition', 'attachment; filename=' + fileName);
            res.set('Content-Type', 'text/plain');
            readStream.pipe(res);
             res.redirect(`/success`);  //THEN THIS
            //--------------------------------------

            }).catch( .....

当您使用 app.use 方法将中间件添加到 express(基于 connect)时,您是在 connect 中将项目附加到 Server.prototype.stack。 当服务器收到请求时,它会遍历堆栈,调用 (request, response, next) 方法。

问题是,如果其中一个中间件项写入响应 body 或 headers(出于某种原因看起来像是 either/or),但实际上并没有t 调用 response.end() 然后调用 next() 然后当核心 Server.prototype.handle 方法完成时,它会注意到:

there are no more items in the stack, and/or
that response.headerSent is true.

因此,它会引发错误。但是它抛出的错误只是这个基本的响应(来自connect http.js源代码:

res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Cannot ' + req.method + ' ' + req.url);

有问题的中间件设置响应header而不调用response.end()并调用next(),这混淆了express的服务器。 所以你设置 header 到 res.render() 。现在如果你想再次渲染它会抛出一个错误。

   

app.get('/success',(req,res)=> {
   res.render("container/index",{waiting:"waiting",......});
  //handle your task then in client side index.ejs with appropriate setTimeout(()=>{},2000) for the waiting div , show waiting div for 2 seconds
});
 //then your actual success gets render

res.render() 函数编译您的模板,在那里插入局部变量,并从这两个东西中创建 html 输出。这就是为什么会出现错误。 不要使用它两次,因为它会发送响应。