使用Express JS时发生NodeJS错误|“错误[错误_HTTP_HEADERS_SENT]:将标头发送到客户端后无法设置标头”

nwlqm0z1  于 2022-12-03  发布在  Node.js
关注(0)|答案(4)|浏览(262)

我开始学习开发API的基础知识,并跟随YouTube上一个名为Ania Kubow的开发者的视频。有三个javascript库在使用中,分别是ExpressJS、Cheerio和Axios。我已经学会了她告诉我们的内容,并且进展顺利。然而,到目前为止,我在执行代码时遇到了一个错误。下面是示例API的主要部分:
注:“app”变量是指Express JS

app.get('/news', (req, res) => {
    axios.get('https://www.theguardian.com/environment/climate-crisis')
        .then((response) => {

            // Store the html retrieved via the axios http GET request
            const html = response.data;

            // Load the retrieved html into the cheerio instance and store to $ cheerio selector
            const $ = cheerio.load(html);

            // DEBUG purpose stuff
            var loopCounter = 0;
            var climate_A_Elements = $('a:contains(climate)', html).length;
            console.log('Number of articles found: ' + climate_A_Elements);
            //

            // Get all articles that contain 'climate' in the <a /> element
            var allFoundArticles = $('a:contains("climate")', html);

            // Iterate through all found articles using cheerio forEach loop
            allFoundArticles.each(function () {

                // Assign article title and url it is located at to variables
                var title = $(this).text();
                var url = $(this).attr('href');

                // Push the previously defined vars to the previously defined array
                articlesData.push({
                    title,
                    url,
                });

                // Output the article details to page as response
                res.json(articlesData);

                // Add to loop count for debugging purposes and log in console
                loopCounter += 1;
                console.log('Loops: ' + loopCounter);

            }).catch(err => {
                    console.log(err);
            })
        })
})

完成Cheerio每个循环的一次迭代后,应用程序出错并给出以下错误输出:

node:internal/errors:484
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at new NodeError (node:internal/errors:393:5)
    at ServerResponse.setHeader (node:_http_outgoing:644:11)
    at ServerResponse.header (C:\Users\etomm\Downloads\Daniel\Dev\Web\climate-change-api\node_modules\express\lib\response.js:794:10)
    at ServerResponse.json (C:\Users\etomm\Downloads\Daniel\Dev\Web\climate-change-api\node_modules\express\lib\response.js:275:10)
    at Element.<anonymous> (C:\Users\etomm\Downloads\Daniel\Dev\Web\climate-change-api\index.js:65:21)
    at LoadedCheerio.each (C:\Users\etomm\Downloads\Daniel\Dev\Web\climate-change-api\node_modules\cheerio\lib\api\traversing.js:519:26)
    at C:\Users\etomm\Downloads\Daniel\Dev\Web\climate-change-api\index.js:52:30
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

Node.js v18.12.1
[nodemon] app crashed - waiting for file changes before starting...

我当然,留下了一个问题,作为对上述视频的评论,但实际上我不希望收到回应,所以在试图找到一段时间的问题后,决定尝试在这里获得一些帮助和指导。
如有任何指导,我们将不胜感激。如果您需要任何帮助,请询问。
谢谢你,谢谢
我尝试更改视频中提到的节点版本,看看是否是版本问题,但没有解决任何问题。我尝试重构代码,并逐步通过几次没有结果,并尝试搜索网页,但无法找到这个特定问题的答案。

v09wglhw

v09wglhw1#

Asad's answer正确;在第一次迭代之后请求就完成了,所以当第二次迭代发生时,Express会抱怨您试图对已经完成的响应进行第二次响应。
最小故障模式为:

app.get("/news", (req, res) => {
  for (let i = 0; i < 2; i++) {
    res.json({message: "this won't work"});
  }
});

修复方法是:

app.get("/news", (req, res) => {
  for (let i = 0; i < 2; i++) {
    // do whatever you need to in the loop
  }

  // respond once after the loop is finished
  res.json({message: "this works"});
});

下面是一个完整的、可运行的示例:

const app = require("express")();
const axios = require("axios");
const cheerio = require("cheerio");

app.get("/news", (req, res) => {
  axios
    .get("https://www.theguardian.com/environment/climate-crisis")
    .then((response) => {
      // Store the html retrieved via the axios http GET request
      const html = response.data;

      // Load the retrieved html into the cheerio instance and store to $ cheerio selector
      const $ = cheerio.load(html);

      // DEBUG purpose stuff
      var loopCounter = 0;
      var climate_A_Elements = $("a:contains(climate)", html).length;
      console.log("Number of articles found: " + climate_A_Elements);
      //

      // Get all articles that contain 'climate' in the <a /> element
      var allFoundArticles = $('a:contains("climate")', html);

      // Iterate through all found articles using cheerio forEach loop
      ///////////////////////////
      const articlesData = []; // <--------- Added
      ///////////////////////////

      allFoundArticles
        .each(function () {
          // Assign article title and url it is located at to variables
          var title = $(this).text();
          var url = $(this).attr("href");

          // Push the previously defined vars to the previously defined array
          articlesData.push({
            title,
            url,
          });

          // Add to loop count for debugging purposes and log in console
          loopCounter += 1;
          console.log("Loops: " + loopCounter);
        })

      // Output the article details to page as response

      //////////////////////////
      res.json(articlesData); // <--------- Moved down
      //////////////////////////
    });
});
app.listen(3000);

测试:

$ curl localhost:3000/news | jq 'length'
14
$ curl localhost:3000/news | jq '.[0].title'
"Australia news  Australia???s banks likely to reduce lending to regions and sectors at risk of climate change impacts, regulator says "

删除无关代码会得到一个非常简单的结果:

const app = require("express")();
const axios = require("axios");
const cheerio = require("cheerio");

app.get("/news", (req, res) => {
  const url = "https://www.theguardian.com/environment/climate-crisis";
  axios.get(url).then(({data}) => {
    const $ = cheerio.load(data);
    const articles = [...$('a:contains("climate")')].map(e => ({
      title: $(e).text().trim(),
      url: $(e).attr("href"),
    }));
    res.json(articles);
  })
  .catch(err => {
    res.status(500).json({message: err.message});
  });
});
app.listen(3000);

或者与async/await一起使用:

app.get("/news", async (req, res) => {
  try {
    const url = "https://www.theguardian.com/environment/climate-crisis";
    const {data} = await axios.get(url);
    const $ = cheerio.load(data);
    const articles = [...$('a:contains("climate")')].map(e => ({
      title: $(e).text().trim(),
      url: $(e).attr("href"),
    }));
    res.json(articles);
  }
  catch (err) {
    res.status(500).json({message: err.message});
  }
});
f87krz0w

f87krz0w2#

您正在一个请求中多次发送响应尝试将结果推入一个数组,然后在循环成功执行后发送它。请尝试以下代码。

app.get('/news', (req, res) => {
  axios.get('https://www.theguardian.com/environment/climate-crisis')
    .then((response) => {
      // artical data array
      const articlesData = [];
      // Store the html retrieved via the axios http GET request
      const html = response.data;

      // Load the retrieved html into the cheerio instance and store 
      //to $ cheerio selector
      const $ = cheerio.load(html);

      // DEBUG purpose stuff
      var loopCounter = 0;
      var climate_A_Elements = $('a:contains(climate)', html).length;
      console.log('Number of articles found: ' + climate_A_Elements);
      //

      // Get all articles that contain 'climate' in the <a /> element
      var allFoundArticles = $('a:contains("climate")', html);

      // Iterate through all found articles using cheerio forEach 
      //loop
      allFoundArticles.each(function () {

        // Assign article title and url it is located at to variables
        var title = $(this).text();
        var url = $(this).attr('href');

        // Push the previously defined vars to the previously defined 
        //array
        articlesData.push({
          title,
          url,
        });

        // Output the article details to page as response

        // Add to loop count for debugging purposes and log in 
        //console
        loopCounter += 1;
        console.log('Loops: ' + loopCounter);

      })
      res.json(articlesData);
    }).catch(err => {
      console.log(err);
    })
})
vecaoik1

vecaoik13#

你应该经常问自己为什么会出现这个错误,在你的例子中,因为你把res.json放进了循环中,你应该总是调用res. json/发送一次响应,人们的建议会对你有所帮助,

drnojrws

drnojrws4#

我遇到了同样的错误。您正尝试多次使用res.json(articlesData)发送响应
试试这个:

app.get('/news', (req, res) => {
  axios.get('https://www.theguardian.com/environment/climate-crisis')
      .then((response) => {

          // Store the html retrieved via the axios http GET request
          const html = response.data;

          // Load the retrieved html into the cheerio instance and store to $ cheerio selector
          const $ = cheerio.load(html);

          // DEBUG purpose stuff
          var loopCounter = 0;
          var climate_A_Elements = $('a:contains(climate)', html).length;
          console.log('Number of articles found: ' + climate_A_Elements);
          //

          // Get all articles that contain 'climate' in the <a /> element
          var allFoundArticles = $('a:contains("climate")', html);

          // Iterate through all found articles using cheerio forEach loop
          allFoundArticles.each(function () {

              // Assign article title and url it is located at to variables
              var title = $(this).text();
              var url = $(this).attr('href');

              // Push the previously defined vars to the previously defined array
              articlesData.push({
                  title,
                  url,
              });

              // Output the article details to page as response

              // Add to loop count for debugging purposes and log in console
              loopCounter += 1;
              console.log('Loops: ' + loopCounter);

          }).then(res.json(articlesData))
          .catch(err => {
                  console.log(err);
          })
      })
})

相关问题