错误:在将标头发送到客户端后无法设置标头

新手上路,请多包涵

我对 Node.js 还很陌生,但遇到了一些问题。

我正在使用 Node.js 4.10 和 Express 2.4.3。

当我尝试访问 http://127.0.0.1:8888/auth/facebook 时,我将被重定向到 http://127.0.0.1:8888/auth/facebook_callback

然后我收到以下错误:

Error: Can't render headers after they are sent to the client.
    at ServerResponse.<anonymous> (http.js:573:11)
    at ServerResponse._renderHeaders (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:64:25)
    at ServerResponse.writeHead (http.js:813:20)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:28:15
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:113:13
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/strategyExecutor.js:45:39)
    at [object Object].pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:32:3)
    at [object Object].halt (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:29:8)
    at [object Object].redirect (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:16:8)
    at [object Object].<anonymous> (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:77:15)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:195:11)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13)
    at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10)
    at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13)
    at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10)
    at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3)
    at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3)
    at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9

node.js:134
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9
    at Array.<anonymous> (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session/memory.js:57:7)
    at EventEmitter._tickCallback (node.js:126:26)

以下是我的代码:

var fbId= "XXX";
var fbSecret= "XXXXXX";
var fbCallbackAddress= "http://127.0.0.1:8888/auth/facebook_callback"

var cookieSecret = "node";     // enter a random hash for security

var express= require('express');
var auth = require('connect-auth')
var app = express.createServer();

app.configure(function(){
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser());
    app.use(express.session({secret: cookieSecret}));
    app.use(auth([
        auth.Facebook({
            appId : fbId,
            appSecret: fbSecret,
            callback: fbCallbackAddress,
            scope: 'offline_access,email,user_about_me,user_activities,manage_pages,publish_stream',
            failedUri: '/noauth'
        })
    ]));
    app.use(app.router);
});

app.get('/auth/facebook', function(req, res) {
  req.authenticate("facebook", function(error, authenticated) {
    if (authenticated) {
      res.redirect("/great");
      console.log("ok cool.");
      console.log(res['req']['session']);
    }
  });
});

app.get('/noauth', function(req, res) {
  console.log('Authentication Failed');
  res.send('Authentication Failed');
});

app.get('/great', function( req, res) {
  res.send('Supercoolstuff');
});

app.listen(8888);

我可以知道我的代码有什么问题吗?

原文由 DjangoRocks 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

Express 中的 res 对象是 Node.js 的 http.ServerResponse 的子类( 阅读 http.js 源代码)。您可以随时调用 res.setHeader(name, value) 直到调用 res.writeHead(statusCode) 。在 writeHead 之后,标头被烘焙,您只能调用 res.write(data) ,最后调用 res.end(data)

错误“错误:发送后无法设置标头。”表示您已经处于 Body 或 Finished 状态,但某些函数试图设置 header 或 statusCode。当您看到此错误时,请尝试查找在某些正文已写入之后尝试发送标头的任何内容。例如,查找意外调用两次的回调,或在发送正文后发生的任何错误。

在您的情况下,您调用 res.redirect() ,这导致响应变为 Finished。然后你的代码抛出了一个错误( res.reqnull )。并且由于错误发生在您的实际 function(req, res, next) (不在回调中),Connect 能够捕获它,然后尝试发送 500 错误页面。但是由于标头已经发送,Node.js 的 setHeader 抛出了您看到的错误。

Node.js/Express 响应方法的完整列表以及何时必须调用它们:

响应必须在 Head 中并保持在 Head 中:

  1. res.writeContinue()

  2. res.statusCode = 404

  3. res.setHeader(name, value)

  4. res.getHeader(name)

  5. res.removeHeader(name)

  6. res.header(key[, val]) (仅限 Express)

  7. res.charset = 'utf-8' (仅限 Express;仅影响 Express 特定的方法)

  8. res.contentType(type) (仅限 Express)

响应必须在 Head 中并变为 Body

  1. res.writeHead(statusCode, [reasonPhrase], [headers])

响应可以在 Head/Body 中并保留在 Body 中:

  1. res.write(chunk, encoding='utf8')

响应可以在 Head/Body 中并变为 Finished

  1. res.end([data], [encoding])

响应可以是 Head/Body 并保持其当前状态:

  1. res.addTrailers(headers)

响应必须在 Head 中并变为 Finished

  1. return next([err]) (仅限 Connect/Express)

  2. 中间件 function(req, res, next) (仅限 Connect/Express)

  3. res.send(body|status[, headers|status[, status]]) (仅限 Express)

  4. res.attachment(filename) (仅限 Express)

  5. res.sendfile(path[, options[, callback]]) (仅限 Express)

  6. res.json(obj[, headers|status[, status]]) (仅限 Express)

  7. res.redirect(url[, status]) (仅限 Express)

  8. res.cookie(name, val[, options]) (仅限 Express)

  9. res.clearCookie(name[, options]) (仅限 Express)

  10. res.render(view[, options[, fn]]) (仅限 Express)

  11. res.partial(view[, options]) (仅限 Express)

原文由 yonran 发布,翻译遵循 CC BY-SA 4.0 许可协议

此问答中的某些答案是错误的。接受的答案也不是很“实用”,所以我想发布一个用更简单的术语解释事情的答案。我的回答将涵盖我一遍又一遍看到的 99% 的错误。对于错误背后的实际原因,请查看已接受的答案。


HTTP 使用一个循环,每个请求需要一个响应。当客户端发送请求(例如 POST 或 GET)时,服务器应该只返回一个响应给它。

此错误消息:

错误:发送后无法设置标头。

通常在您为一个请求发送多个响应时发生。确保每个请求仅调用一次以下函数:

  • res.json()
  • res.send()
  • res.redirect()
  • res.render()

(还有一些很少使用的,检查接受的答案)

调用这些 res 函数时,路由回调不会返回。它将继续运行,直到到达函数末尾或 return 语句。如果您想在发送回复时返回,您可以这样做: return res.send()


以这段代码为例:

 app.post('/api/route1', function(req, res) {
  console.log('this ran');
  res.status(200).json({ message: 'ok' });
  console.log('this ran too');
  res.status(200).json({ message: 'ok' });
}

当 POST 请求发送到 /api/route1 时,它将运行回调中的每一行。将抛出 Can’t set headers after they are sent 错误消息,因为 res.json() 被调用两次,这意味着发送了两个响应。

每个请求只能发送一个响应!


上面代码示例中的错误很明显。一个更典型的问题是当你有多个分支时:

 app.get('/api/company/:companyId', function(req, res) {
  const { companyId } = req.params;
  Company.findById(companyId).exec((err, company) => {
      if (err) {
        res.status(500).json(err);
      } else if (!company) {
        res.status(404).json();      // This runs.
      }
      res.status(200).json(company); // This runs as well.
    });
}

这条带有回调的路由在数据库中找到一家公司。当查询不存在的公司时,我们将进入 else if 分支并发送 404 响应。在那之后,我们将继续下一个也发送响应的语句。现在我们已经发送了两个响应,并且会出现错误消息。我们可以通过确保只发送一个响应来修复此代码:

 .exec((err, company) => {
  if (err) {
    res.status(500).json(err);
  } else if (!company) {
    res.status(404).json();         // Only this runs.
  } else {
    res.status(200).json(company);
  }
});

或者在发送响应时返回:

 .exec((err, company) => {
  if (err) {
    return res.status(500).json(err);
  } else if (!company) {
    return res.status(404).json();  // Only this runs.
  }
  return res.status(200).json(company);
});


一个大罪人是异步函数。从 这个 问题中获取函数,例如:

 article.save(function(err, doc1) {
  if (err) {
    res.send(err);
  } else {
    User.findOneAndUpdate({ _id: req.user._id }, { $push: { article: doc._id } })
    .exec(function(err, doc2) {
      if (err) res.send(err);
      else     res.json(doc2);  // Will be called second.
    })

    res.json(doc1);             // Will be called first.
  }
});

这里我们在代码示例中有一个异步函数 ( findOneAndUpdate() )。如果没有错误( errfindOneAndUpdate() 将被调用。因为这个函数是异步的,所以 res.json(doc1) 将被立即调用。假设 findOneAndUpdate() 中没有错误。然后将调用 res.json(doc2) 中的 else 。现在已经发送了两个响应,并且出现了 Can’t set headers 错误消息。

在这种情况下,修复方法是删除 res.json(doc1) 。要将这两个文档发送回客户端, res.json() 在 else 中可以写成 res.json({ article: doc1, user: doc2 })

原文由 Mika Sundland 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题