首页 > 解决方案 > 将标准输出用于 node.js / express 时出现 ERR_HTTP_HEADERS_SENT 错误

问题描述

在我的节点应用程序中遇到有据可查的“错误 [ERR_HTTP_HEADERS_SENT]:在将标头发送到客户端后无法设置标头”。我已经查看了专门针对此问题的现有页面并尝试了他们的补救措施 - 主要是确保“返回”所有其他回复;但是错误仍然存​​在。代码如下。

你会注意到我有一堆不同的路径通过子进程调用独特的 python 函数。有趣的是,http 错误只出现在某些路径上——通常是那些输出非常大的路径。在所有情况下,响应都可以很好地到达并在客户端呈现,但是对于较大的响应,我会收到 http 错误并且我的应用程序会关闭。我可以想到一些潜在的非中间件原因 - 随着响应变大, res.send() 的行为可能会有所不同?或者可能是 subprocess.stdout 导致较大响应出现问题?或者,较长的响应时间可能会导致浏览器在响应交付之前重新发送请求?...希望在深入研究潜在的中间件问题之前排除这些。谢谢

router.get('/element/chart', ensureAuthenticated, (req,res) => {
    const path = require('path')
    const {spawn} = require('child_process')
    console.log(current_data_page_id)
    console.log(typeof current_data_page_id)
    let runScript;

    runScript = (current_data_page_id) => {
        switch(current_data_page_id) {
            case "100":
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_1.py')]); 
            break;
            case "101":
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_2.py')]);
            break;
            case "102":
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_3.py')]);
            break;
            case "103":
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_4.py')]);
            break;
            case "104":
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_5.py')]);
            break;
            case "200":
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_6.py')]);
            break;
            case "201": 
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_7.py')]);
            break;
            case "202": 
                return spawn('python', ["-u", path.join(process.cwd(),'/python/python_func_8.py')]);
            break;
            default:
                console.log("Data page ID does not match current options") 
        };
    };
    
    const subprocess = runScript(current_data_page_id)
    // print output of script
    subprocess.stdout.on('data', (data) => {
        var dataToSend = data.toString();
        console.log(dataToSend);
        res.send(dataToSend);
        res.end('end')
        return;
    });
    subprocess.stderr.on('data', (data) => {
        console.log(`error:${data}`);
        return;
    });
    subprocess.stderr.on('close', () => {
        console.log("Closed");
        return;
    });

});

// ensureAuthenticated middleware
module.exports = {
    ensureAuthenticated: function(req, res, next) {  
        if(req.isAuthenticated()) {
            return next();
        }
        req.flash('error_msg', 'Please log in to view this resource');
        res.sendFile(process.cwd() + '/views/login.html');
        return;
    }
}

下面的控制台输出。堆栈跟踪指向“res.send(dataToSend);” 线。

_http_outgoing.js:536
    throw new ERR_HTTP_HEADERS_SENT('set');
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

标签: node.jsexpressstdout

解决方案


错误的原因是你多次调用“结束”请求,一个http请求只需要完成一次。你打电话res.send(dataToSend);或者res.end('end')这意味着你完成了请求 2 次。

当您删除res.end('end');行时,错误仍然出现,因为res.send(dataToSend);已多次调用,因为我认为您的“python”命令超过1行数据,然后subprocess.stdout.on('data'已被多次调用。

这种情况的想法是在“命令”完成(完成)时结束请求,您可以使用进程事件(不是)捕获“完成closespawn事件subprocess.stdout

对于数据(命令的输出),我们有两种方法:

  • 将输出附加到字符串变量并在命令完成后将其发送到客户端
var data = ''; // init data store
subprocess.stdout.on('data', (data) => {
  var dataToSend = data.toString();
  console.log(dataToSend);
  data += dataToSend + '\n'; // append data with new line char :)
  // res.send(dataToSend);
  // res.end('end')
  // return;
});

// subscribe to close event
subprocess.on('close', (code) => {
  if (code !== 0) {
    console.log(`grep process exited with code ${code}`);
  }
  res.send(data); // send data to client and finish the request
});
  • 使用 express api 编写对res对象的响应(我喜欢这种方式)
subprocess.stdout.on('data', (data) => {
  var dataToSend = data.toString();
  console.log(dataToSend);
  res.write(dataToSend); // write data to response stream
  // res.send(dataToSend);
  // res.end('end')
  // return;
});

// subscribe to close event
subprocess.on('close', (code) => {
  if (code !== 0) {
    console.log(`grep process exited with code ${code}`);
  }
  res.end(); // finish the request, `end` not `send`
});

推荐阅读