【问题标题】:Error: Error: Can't set headers after they are sent错误:错误:发送后无法设置标头
【发布时间】:2017-02-17 03:33:30
【问题描述】:

我正在使用 express 和 node.js

目标:当用户访问 /index 时,如果 url 中有特定的跟踪代码,则将其重定向到 /welcome。函数redirectBasedReferrer根据跟踪代码进行重定向。

index: function (req, res, next) {
    // We can't cache the index because it serves different things depending on cookies.
    res.set('X-Frame-Options', 'SAMEORIGIN');
    CacheControl.private().maxAge(0).setHeaders(res);

    if (!req.imsProfile && !this.redirectBasedReferrer(req, res, next)) {
        return this.about(req, res, next);
    }
    res.render('spark', {
        analyticsURI: Settings.getSetting('analytics').siteCatalystScriptURI
    });
},

redirectBasedReferrer: function (req, res, next) {
    if (!req || !req.query) {
        return false;
    }
    var referringtrackingCode = req.query.promoid;
    if (!referringtrackingCode || referringtrackingCode != "SYBNM49C") {
        return false;
    }
    return res.redirect('/welcome');
},

当代码到达 redirectBasedReferrer 并将用户重定向到 /welcome 时,我收到错误:

/?promoid=SYBNM49C&mv=other:错误:错误:发送后无法设置标头。
在 ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)

【问题讨论】:

  • 我有点担心中间件函数会返回值,我认为你不应该在 redirectBasedReferrer 中有这些返回语句
  • 基本上,当人们访问 /index 时,如果 URL 中有特定的跟踪代码,他们可能会被重定向到 /welcome
  • 当然,我明白了。但是中间件返回语句并没有真正做任何事情,它们是回调,所以返回值没有任何用处。如果redirectBasedReferrer 是一个中间件,它可以通过将属性分配给reqres 对象来传递类似布尔值的值,但不能通过返回值。

标签: javascript node.js express


【解决方案1】:

此错误意味着您以某种方式设法在同一路由中返回了两次响应。对我来说,这种情况发生的地方并不是很明显,但我会说向用户返回错误响应可能是有意义的,而不是只返回 false。但除此之外,也许您在其他地方有一些中间件错误地过早地向用户返回响应但没有结束进程,因此它继续尝试再次向用户返回响应并抛出错误。

编辑:

现在您已经更新了代码,我看到了错误。这一行:

if (!req.imsProfile && !this.redirectBasedReferrer(req, res, next)) {

将导致中间件潜在地重定向用户,但仍继续执行索引路由,从而导致错误。

【讨论】:

  • 正如 Ananth 所说,“redirectBasedReferrer”方法不需要将重定向返回到欢迎页面。重定向应该在索引路由中。 “redirectBasedReferrer”函数只是你创建的一个内部函数,它应该向索引路由返回真或假,然后可以决定如何继续。
  • 其次,redirectBasedReferrer 看起来更像是一个独立的函数,而不是一个中间件。
  • 重定向,应该是return res.redirect('/welcome');
  • 那行是正确的。它只是在错误的地方。从“redirectBasedReferrer”返回 true 或 false 后,您可以根据返回的值进行重定向或 res.render。
【解决方案2】:

最大的问题仍然是这里的 if 块,但是您还需要更改一些其他代码。

if (!req.imsProfile && !this.redirectBasedReferrer(req, res, next)) {
    return this.about(req, res, next);
}

就像上面 Sam 说的那样,index 路由中的这一行会导致 redirectBasedReferrer 中间件中的重定向发送响应,以及正文中的 about 中间件的 if 语句。事实上,index 中间件中的代码实际上也会通​​过 if 块之后的行发送第三个响应:

res.render('spark', {
    analyticsURI: Settings.getSetting('analytics').siteCatalystScriptURI
});

使用 express 中间件时,最好在发送响应时使用 if-else 块,这样您将始终发送一个或另一个响应,而不是多个。

if (!req.imsProfile ...) {
    ...
} else {
    res.render('spark', {
        analyticsURI: Settings.getSetting('analytics').siteCatalystScriptURI
    });
}

注意前面代码块中的省略号。这些也是您需要稍作改动的部分。

与作为中间件相比,我认为 redirectBasedReferrer 作为独立函数会更好地工作,该函数根据 url 中是否有跟踪代码返回真或假。因此,如果你将 redirectBasedReferrer 变成一个独立的函数,它只接收 req 对象(这是你在函数内检查的唯一对象),你可能可以在同一个块中使用它,如下所示:

if (req.imsProfile) {
    res.render('spark', {
        analyticsURI: Settings.getSetting('analytics').siteCatalystScriptURI
    });
} else if (this.redirectBasedReferrer(req)) {
    res.redirect('/welcome');
} else {
    res.redirect('/about');
}

请注意,在这种情况下,redirectBasedReferrer 将根据您现在的内容稍作修改,因此它看起来像这样:

redirectBasedReferrer: function (req) {
    if (!req || !req.query) {
        return false;
    }
    var referringtrackingCode = req.query.promoid;
    if (!referringtrackingCode || referringtrackingCode != "SYBNM49C") {
        return false;
    }
    return true;
}

【讨论】:

  • 仅供参考,我假设为了编写示例,您的代码中使用的 this.about 是用于处理“/about”路由的中间件
猜你喜欢
  • 2018-06-08
  • 2018-06-29
  • 2013-12-22
  • 1970-01-01
  • 2017-12-22
  • 2015-08-10
  • 2018-09-02
相关资源
最近更新 更多