繁体   English   中英

Node.js无法在发送标头后设置标头引入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( .....

我评论为“从服务器内存中发送文件的聪明方法”的代码来自这篇文章: Node Express.js-从内存中下载文件-“文件名必须是字符串”

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

该代码曾经可以工作。

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

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

然后,我尝试添加另一个res.render()[在这种情况下为res.render('success.ejs'); ]代码之前和之后将.txt文件发送到客户端。

错误仍然存​​在。 同样,也没有重定向到success.ejs,换句话说,就是res.render('success.ejs'); 即使将其放在那段代码之后,也从来没有奏效。

您将需要检查express.js源代码( 在此处 ):

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(...)源代码 ),然后将done传递给tryInitView等。

最后,它将在成功的情况下使用str调用done回调,在失败的情况下使用err进行调用。 然后,它会在done回调中触发res.send() ,这会阻止您在此之后设置标头。

  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方法添加中间件来表达(基于connect)时,您将在connect Server.prototype.stack项目附加到Server.prototype.stack 服务器收到请求后,将遍历堆栈,并调用(request,response,next)方法。

问题是,如果在其中一个中间件项中写入响应主体或标头(它看起来是/或某种原因),但不调用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);

有问题的中间件无需调用response.end()即可设置响应头,而无需调用next(),这会使express的服务器感到困惑。 因此,您可以通过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输出。 这就是为什么会出现错误。 不要两次使用它,因为它发送响应。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM