问题描述

"Cannot set headers after they are sent to the client" 是由于代码在发送响应后,尝试设置 HTTP 头而引起的错误。这意味着函数已经发送了一个或多个响应(如 HTTP 响应头、HTTP 正文或 JSON 字符串),然后试图发送另一个响应。

问题分析

可能是因为代码出现了异步回调并且重复发送响应或通过两个或更多的代码路径向客户端发送响应导致此错误。如下所示:

app.get('/example', function(req, res) {
    res.send('Hello World!');
    res.status(200).json({ message: 'Invalid request' }); // 错误:不能设置 header
});

解决方案

要解决该问题,您可以确保一段代码的路经仅仅会发送一次响应,并避免出现重复响应。在异步操作中进行响应时,最好使用 Promise 或者 async/await 来处理控制流程。

另外,也可以通过返回状态码或自定义错误消息来反映错误,而无需通过 res.json()res.send() 等方法发送响应,以便避免重复响应和 header 修改。

以下是对上述问题的改进示例:

app.get('/example', async (req, res) => {
  try {
    const result = await someAsyncOperation();
    // 对结果进行处理,然后将其作为响应发送给客户端
    res.status(200).json(result);
  } catch (error) {
    // 发生错误时,返回自定义错误消息
    res.status(500).json({ message: 'Error occurred while processing request' });
  }
});

在这个示例中,我们使用 async/await关键字来处理异步操作。如果操作成功完成,将返回一些数据,否则,将捕获异常,并发送一个自定义的错误消息。

最后,我们在向客户端发送响应时只发送一次响应。

补充(网上常见的2个问题解决方案):

1. req-res未形成闭环,把next去掉即可

router.get('/',function(req,res,next){
    console.log("Get a get req");
    res.send("hello lcq!");
    next();
}

2. 异步与同步问题:

问题描述

NodeJs+express+mongoDB接口中,在循环中写`res.json`时会报错(Cannot set headers after they are sent to the client)

解决方法

  • 循环外面定义一个变量为`false`
  • 在循环中要写`res.json`的地方让其变为`true`
  • 在循环外面判断该变量为`true`时写`res.json`

具体代码实现

var isShow = false;   //定义开关变量
//loadcurr为数组,具体内容省略了
loadcurr.forEach(item => {
    Stock.findOneAndUpdate({             //Stock是用来连接数据库集合的
       _id: ObjectId(item._id)
        }, {
            $set: {
            //此处省略要修改的内容
                。。。。。。
            }
        },
        function (err, data) {
            if (err) {
                console.log(err)
            } else {
            //此处应该写res.json,但是为了解决报错让前面定义的变量为true
                isShow = true
                console.log('1', isShow);
            }
        })
})
console.log('2', isShow);
if (isShow == true) {
    res.json({
        status: "200"
    })
}

这样就可以成功解决Cannot set headers after they are sent to the client的错啦!!!

注意:

如果上面的代码执行顺序是先打印了console.log('2', isShow);后打印console.log('1', isShow);,说明存在异步,因此用异步解决即可,具体实现如下所示(若顺序是先执行console.log('1', isShow);后执行console.log('2', isShow);就是正确的):

若返回值不是promise用以下方法

var isShow = false;
    (async function(){
        await new Promise((resolve, reject) => {
            //loadcurr为数组,具体内容省略了
            loadcurr.forEach(item => {
                //Stock是用来连接数据库集合的
                Stock.findOneAndUpdate({
                    _id: ObjectId(item._id)
                }, {
                    $set: {
                        //此处省略要修改的内容
                        。。。。。。
                    }
                },
                function (err, data) {
                    if (err) {
                        console.log(err)
                    } else {
                        resolve(isShow = true)
                        console.log('1', isShow);
                    }
                })
            })
        })
        console.log('2', isShow);
            if (isShow == true) {
                res.json({
                    status: "200"
                })
            }
    })()

若返回值是promise用以下方法

var isShow = false;
    (async function(){
        //loadcurr为数组,具体内容省略了
        await loadcurr.forEach(item => {
            //Stock是用来连接数据库集合的
            _id: ObjectId(item._id)
        }, {
              $set: {
              //此处省略要修改的内容
              。。。。。。
           }
         },
            function (err, data) {
                if (err) {
                    console.log(err)
                } else {
                    isShow = true
                    console.log('1', isShow);
                }
            })
        })
        console.log('2', isShow);
            if (isShow == true) {
                res.json({
                    status: "200"
                })
            }
    })()