Node.js发送后不能发送标题

我试图做一个小的应用程序,为给定的用户的照片大拇指500px.com(一个照片网站)rss饲料。 我对这一切都比较陌生。

我在本地testing时看到了一个我不明白的行为。 页面首次加载罚款,但重新加载时,我得到“发送后不能发送标头”。 从这里的其他答案看来,这通常是由双重callback造成的。 我试过把一些if (err) res.send(); 在不同的地方发表声明,但似乎没有解决问题。 我也尝试重新configuration代码的结构,这似乎也没有工作。

谁能比我更有经验的人知道这里发生了什么?

 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对象,所以在第二次路由命中后,你有两个侦听器,因此当你发射,你有两个听众被调用,你试图发送两个响应,其中一个响应对象已经发送了响应。

这里的核心问题是你试图在请求中使用模块级variables。 这会导致不同请求之间的交叉耦合(这两个请求都试图在处理请求时潜在地使用相同的发射器对象)。 这只是一个竞争状态,等待有多个用户使用您的系统。

我不知道为什么你在这里使用一个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()callback中移动parseString()操作。
  2. 彻底摆脱共享的EventEmitter使用。
  3. 在两个asynchronous操作上添加error handling。

如果出于某种原因,您需要或想要在您的代码中使用EventEmitter (尽pipe当前的代码不显示为什么您需要使用它),您必须为您正在处理的每个请求创build一个新的,并且只存储它在该特定请求的范围内(不在模块级别variables中)。 这既可以防止单独的请求中的串扰,也可以避免将多个侦听器添加到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... ,在请求失败的情况下也应该有error handling,我不知道应用的其他部分看起来像但EventEmitter没有必要在这里。

 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 ... });