【问题标题】:ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the clientERR_HTTP_HEADERS_SENT:发送到客户端后无法设置标头
【发布时间】:2019-02-06 21:19:55
【问题描述】:

在使用 Passport.js、Express 和 Mongoose 时,我在 NodeJS 中遇到了这个奇怪的问题。基本上,即使我不发送多个标头,我也会收到一条错误消息“无法在标头发送到客户端后设置标头”。

我已经阅读了其他帖子并尝试了它们,但它们都没有奏效。

我已经研究了 github 问题,但似乎找不到解决方案。我收到的问题是,当我发送多个响应标头时会触发此错误,但事实是我没有发送多个标头。看起来很奇怪。

这是我的堆栈跟踪:

(node:9236) DeprecationWarning:当前的 URL 字符串解析器已被弃用,并将在未来的版本中删除。要使用新的解析器,请将选项 { useNewUrlParser: true } 传递给 MongoClient.connect。

服务器在 5000 端口上运行
MongoDB 连接错误
[ERR_HTTP_HEADERS_SENT]:无法设置标头后发送到 客户
在 validateHeader (_http_outgoing.js:503:11)
在 ServerResponse.setHeader (_http_outgoing.js:510:3)
在 ServerResponse.header (/Users/lourdesroashan/code/github/devlog/node_modules/express/lib/response.js:767:10)
在 ServerResponse.json (/Users/lourdesroashan/code/github/devlog/node_modules/express/lib/response.js:264:10)
在 Profile.findOne.then.profile (/Users/lourdesroashan/code/github/devlog/routes/api/profile.js:27:30)

这是我的服务器代码:

router.get("/userprofile", passport.authenticate('jwt', { session: false }), (req, res) => {

  Profile.findOne({ user: req.user.id }).then(profile => {
    if (!profile) {
      return res.status(404).json({ error: "No Profile Found" });
    }
    else {
      res.json(profile);
    }
  }).catch(err => {
    console.log(err);
  })
});

我了解错误的含义,但据我所知,我认为我不会发送多个标头,我什至通过 console.log 检查了仅运行了一个块。

提前非常感谢您! :)

完整代码:https://github.com/lourdesr/devlog

编辑:

我想通了。在尝试获取经过身份验证的用户时,这是我的 passport.js 中的一个问题。我忘记在导致它的“完成”方法上使用“返回”。刚刚添加了 return 语句,它就起作用了!

【问题讨论】:

  • 您显示的服务器代码似乎不太可能导致有关发送标头的错误。必须有一些其他代码导致该错误。每当您尝试对同一请求发送多个响应时,就会出现该特定错误,并且通常是由不正确的异步代码引起的。
  • 由于您从 jfriend00 的建议中找到了单独的解决方案,请发布您自己的问题答案并接受它。

标签: node.js express mongoose passport.js express-jwt


【解决方案1】:

当您尝试对同一个请求发送多个响应时,就会发生该特定错误,通常是由不正确的异步代码引起的。

您在问题中显示的代码似乎不会导致该错误,但我确实在不同的路线here 中看到了会导致该错误的代码。

你有这个:

if (!user) {
  errors.email = "User not found";
  res.status(404).json({ errors });
}

您需要将其更改为:

if (!user) {
  errors.email = "User not found";
  res.status(404).json({ errors });
  // stop further execution in this callback
  return;
}

您不希望代码在您完成 res.status(404).json({ errors }); 后继续运行,因为它会尝试发送另一个响应。


此外,你到处都有这个:

if (err) throw err;

在异步回调中,您需要将其替换为实际发送错误响应的内容,例如:

if (err) {
    console.log(err);
    res.sendStatus(500);
    return;
}

在异步回调中抛出只是返回到 node.js 事件系统中,并且不会被抛出到任何你可以实际捕获它的地方。此外,它不会发送对 http 请求的响应。换句话说,它并没有真正做服务器应该做的事情。所以,帮自己一个忙,永远不要在你的服务器中编写代码。当您收到错误时,发送错误响应。


因为看起来您可能是新来的,所以我想称赞您在https://github.com/lourdesr/devlog 包含指向您的完整源代码的链接,因为只有通过查看才能看到错误所在的地方发生。

【讨论】:

  • @lourdesr - 这是否向您解释并回答了您的问题?
  • 它确实给出了更多的澄清。但是,几个小时后,我能够找出问题所在。它潜伏在我的 passport.js 文件中,同时试图获取经过身份验证的用户 - 我没有在“完成”方法之一中添加返回语句,这导致了多个标题错误。感谢您的帮助!
  • 很好,你已经澄清了问题。
  • 这是一个很好的例子,说明了 Node 中 pre-async/await 的处理方式所存在的缺陷
  • 谢谢兄弟! 50分钟后,我终于找到了一条路!
【解决方案2】:

我收到此错误是因为我犯了一个愚蠢的错误。在引用我的其他工作代码时,我需要更加小心。真正令人尴尬的部分是我花了多长时间试图找出错误的原因。哎呀!

不好:

return res
  .send(C.Status.OK)
  .json({ item });

好:

return res
  .status(C.Status.OK)
  .json({ item });

【讨论】:

  • 我看不出你的好例子和坏例子有什么区别。 @TheDarkIn1978
  • 区别在于我调用.send("ok") 的“坏”示例,而我调用.status("ok") 的“好”示例。在.send 之后链接.json 调用会导致错误。这是我的其他一些代码中的一个粗心的复制/粘贴错误。
  • 你救了我!
  • 不敢相信这么多人犯了同样的错误,却无法发现差异
【解决方案3】:

使用 ctrl + F 热键并找到所有“res”。关键词 然后用'return'前缀替换它们,

change  all 'res.' to  'return res.'

类似这样的:

res.send() 改为 --> return res.send()

也许你有“res”。在某个块中,例如 if() 语句

【讨论】:

    【解决方案4】:

    抱歉回复晚了, 根据 mongoose 文档“Mongoose 查询不是承诺。它们有一个 .then() 函数用于 co 和 async/await 作为方便。但是,与承诺不同,调用查询的 .then() 可以多次执行查询”

    所以要使用承诺

    mongoose.Promise = global.Promise //To use the native js promises
    

    然后

    var promise = Profile.findOne({ user: req.user.id }).exec()
    promise.then(function (profile){
        if (!profile) {
          throw new Error("User profile not found") //reject promise with error
        }
        return res.status(200).json(profile) //return user profile      
    }).catch(function (err){
        console.log(err); //User profile not found
        return res.status(404).json({ err.message }) //return your error msg
    })
    

    这是一篇关于switching out callbacks with promises in Mongoose的好文章

    这个关于猫鼬的答案承诺拒绝处理Mongoose right promise rejection handling

    【讨论】:

      【解决方案5】:

      对节点错误 [ERR_HTTP_HEADERS_SET] 有一个简单的修复。您需要在响应前添加return 语句,以确保您的路由器在出错时正确退出:

      router.post("/", async (req, res) => {
          
          let user = await User.findOne({email: req.body.email});   
          if (!user) **return** res.status(400).send("Wrong user");
          
      });
      

      【讨论】:

        【解决方案6】:

        因为在您的请求中发送了多个响应。如果您在 else 条件中使用 return 关键字,您的代码将正常运行

        if (!profile) {
            return res.status(404).json({ error: "No Profile Found" });
        }
        else {
            **return** res.json(profile);
        }
        

        【讨论】:

          【解决方案7】:

          我在 HBS 模板引擎中使用 express 和 mongoose 时遇到了同样的错误。我去了 Expressjs 并阅读了 res.render 的文档,它说 // 如果指定了回调,则必须显式发送呈现的 HTML 字符串。所以我最初并没有在回调中显式发送我的 html。顺便说一句,这仅适用于联系表格,而不是登录信息,尽管是 GET

          //Original
          let { username, email } = req.query;  //My get query data easier to read
          
          res.status(200).render('index', { username, email });
          
          
          //Solution without error. Second param sending data to views, Third param callback
          
          res.status(200).render('index', { username, email }, (err, html)=>{
                res.send(html);
           });
          

          【讨论】:

            【解决方案8】:

            您必须在您的程序中启用Promises,在我的情况下,我使用mongoose.Promise = global.Promise 在我的猫鼬模式中启用了它。 这样就可以使用native js promises

            此解决方案的其他替代方案是:

            var mongoose = require('mongoose');
            // set Promise provider to bluebird
            mongoose.Promise = require('bluebird');
            

            // q
            mongoose.Promise = require('q').Promise;
            

            但您需要先安装这些软件包。

            【讨论】:

              【解决方案9】:

              除了没有返回之外,我的问题是我忘记在处理程序中等待异步函数。所以处理程序正在返回,稍后异步函数完成了它的工作。 ??‍♀️

              之前:

              req.session.set('x', {...});
              req.session.save();
              return req.status(200).end();
              

              当我需要等待时:

              req.session.set('x', {...});
              await req.session.save();
              return req.status(200).end();
              

              【讨论】:

                【解决方案10】:

                在 react 中,如果你在 useEffect 钩子中调用函数,请确保将依赖项添加到依赖项数组中。

                【讨论】:

                  猜你喜欢
                  • 2020-05-20
                  • 2019-06-15
                  • 2019-12-11
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-06-02
                  • 2020-06-21
                  • 2021-07-21
                  • 1970-01-01
                  相关资源
                  最近更新 更多