【问题标题】:Node.js Can't set headers after they are sent Error after introducing res.render()Node.js Can't set headers after they are sent 引入 res.render() 后出错
【发布时间】:2018-08-28 13:53:10
【问题描述】:

我有这段代码:

   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 - Download file from memory - 'filename must be a string'

它的作用是从内存中取出一个字符串并将其作为 .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 之前。

【问题讨论】:

    标签: javascript node.js header request-headers response-headers


    【解决方案1】:

       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) 方法。

    问题是,如果在其中一个中间件项目中写入响应正文或标头(看起来可能是/或出于某种原因),但不调用 response.end() 而您调用 next() 然后为核心 Server.prototype.handle 方法完成,它会注意到:

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

    因此,它会引发错误。但是它抛出的错误只是这个基本响应(来自连接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.redirect('success.ejs');线?但是 res.render('waiting.ejs'); 呢?行吗?
    • 每个响应中间件只能处理一个渲染
    • 只是一条消息和一个动画
    • 你可以添加这两条额外的路线,它会让你的工作完成......并且也看到上面的代码
    • 我希望在 POST 之后运行这两条路线。使用更新后的方法,我不确定这两条路线在 POST 之后将如何更新
    【解决方案2】:

    您必须检查 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()方法时,它会将done回调传递给app.render(...)source code),然后将done传递给tryInitView等等。

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

    【讨论】:

    • 谢谢!那么是否有解决方法,或者如果我想将 txt 文件发送给客户端,我将不得不忘记使用 res.render()s?
    【解决方案3】:

    res.render() 函数编译您的模板,在其中插入本地变量,并从这两件事中创建 html 输出。这就是错误来的原因。 不要使用它两次,因为它会发送响应。

    【讨论】:

      猜你喜欢
      • 2014-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 2015-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多