繁体   English   中英

Node.js在发送后无法发送标头

[英]Node.js can't send headers after they are sent

我正在尝试制作一个小应用程序,为给定用户的照片大拇指刮擦500px.com(一个照片网站)rss feed。 我对这一切都比较新。

我在本地测试时看到了一些我不理解的行为。 页面第一次加载正常,但重新加载时,我收到“发送后无法发送标题”。 从其他答案来看,这似乎通常是由双回调引起的。 我试过把一些if (err) res.send(); 在各个地方的陈述,但它似乎没有解决问题。 我也尝试重新配置代码的结构,这似乎也没有用。

谁比我更有经验可以发现这里发生了什么?

var express = require('express');
var router = express.Router();
var request = require('request');
var parseString = require('xml2js').parseString;
const cheerio = require('cheerio');

var EventEmitter = require('events').EventEmitter;
var body = new EventEmitter();

/* GET home page. */
router.get('/', function(req, res, next) {


  request("https://500px.com/janedoe/rss", function(error, response, data) {
        body.data = data;
        body.emit('update');
  }); 

  body.on('update', function() {

    parseString(body.data, function (err, result) {

        // the stuff below likely isn't relevant to the problem, just some testing

        var photoLink = result.rss.channel[0].item[0].description[0];

        const $ = cheerio.load(photoLink);
        const links = $('img');
        const linkString = links.attr('src').toString();

        // end of area probably not relevant

        res.render('index', { title: 'Express', linkString});

    });



  });

});

module.exports = router;

这里有很多问题。 可能导致您询问的特定错误的原因是,每次您的路线被击中时,您都会向EventEmitter对象添加另一个侦听器,因此在第二次击中路线后,您有两个侦听器,因此当您发出时,您有两个被调用的侦听器,您尝试发送两个响应,其中一个响应已发送响应的旧响应对象。

这里的核心问题是您尝试在请求中使用模块级变量。 这导致不同请求之间的交叉耦合(这两个请求都试图在处理它们的请求时潜在地使用相同的发射器对象)。 只要您有多个用户使用您的系统,这只是一种等待发生的竞争条件。

我根本不知道为什么你在这里使用EventEmitter对象,因为看起来你不需要它。 当请求收集了所有数据而根本不使用EventEmitter时,您可以处理最终结果。 这将解决竞争条件和多个处理程序。

这是一种修复方法:

router.get('/', function(req, res, next) {
    request("https://500px.com/janedoe/rss", function(error, response, data) {
        if (error) {
            next(error);
            return;
        }
        parseString(data, function(err, result) {
            if (err) {
                next(err);
                return;
            }
            // the stuff below likely isn't relevant to the problem, just some testing

            var photoLink = result.rss.channel[0].item[0].description[0];

            const $ = cheerio.load(photoLink);
            const links = $('img');
            const linkString = links.attr('src').toString();

            // end of area probably not relevant

            res.render('index', {
                title: 'Express',
                linkString
            });
        });
    });
});

变更摘要:

  1. request()回调中移动parseString()操作。
  2. 完全摆脱共享的EventEmitter使用。
  3. 在两个异步操作上添加错误处理。

如果由于某种原因,您需要或想要在代码中使用EventEmitter (虽然当前代码没有显示您需要使用的代码),您必须为正在处理的每个请求创建一个新的,并且只存储它在特定请求的范围内(不在模块级变量中)。 这两者都可以防止在飞行中的单独请求之间发生串扰,并防止为同一事件向EventEmitter对象添加多个侦听器,因为每个请求都有一个单独的EventEmitter ,并且只向该EventEmitter对象添加一个侦听器。 这是如何工作的:

/* GET home page. */
router.get('/', function(req, res, next) {
    // make EventEmitter object for this particular request
    let body = new EventEmitter();

    request("https://500px.com/janedoe/rss", function(error, response, data) {
        if (error) {
            next(error);
            return;
        }
        body.data = data;
        body.emit('update');
    });

    body.on('update', function() {

        parseString(body.data, function(err, result) {
            if (err) {
                next(err);
                return;
            }

            // the stuff below likely isn't relevant to the problem, just some testing

            var photoLink = result.rss.channel[0].item[0].description[0];

            const $ = cheerio.load(photoLink);
            const links = $('img');
            const linkString = links.attr('src').toString();

            // end of area probably not relevant

            res.render('index', {
                title: 'Express',
                linkString
            });
        });
    });
});

看起来你可以在body.on('update...移动所有body.on('update...到请求的回调中。在请求失败的情况下你也应该有错误处理。我不知道应用程序的其余部分是什么看起来像但这里不需要EventsEmitter。

var express = require('express');
var router = express.Router();
var request = require('request');
var parseString = require('xml2js').parseString;
const cheerio = require('cheerio');

var EventEmitter = require('events').EventEmitter;
var body = new EventEmitter();

/* GET home page. */
router.get('/', function(req, res, next) {
  request("https://500px.com/janedoe/rss", function(error, response, data) {
    parseString(data, function (err, result) {

      // the stuff below likely isn't relevant to the problem, just some testing

      var photoLink = result.rss.channel[0].item[0].description[0];

      const $ = cheerio.load(photoLink);
      const links = $('img');
      const linkString = links.attr('src').toString();

      // end of area probably not relevant

      res.render('index', { title: 'Express', linkString});

    });
  }); 
});

module.exports = router;

您可以在路由处理程序中移动EventEmitter以避免这种情况。

只需剪切此行:

var body = new EventEmitter();

并将其粘贴到您的路线中:

router.get('/', function(req, res, next) {

  var body = new EventEmitter();          // Paste it here
  request("https://500px.com/janedoe/rss", function(error, response, data) {
        body.data = data;
        body.emit('update');
  }); 

  // The rest of your code here ...

});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM