Express/Node - 在响应已发送给客户端之后无法设置响应头

9 浏览
0 Comments

Express/Node - 在响应已发送给客户端之后无法设置响应头

我对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. (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]. (/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. (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. (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. (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. (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. (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. (/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);

请问我的代码有什么问题吗?

admin 更改状态以发布 2023年5月24日
0
0 Comments

Q&A中的一些答案是错误的。已接受的答案也不太“实用”,因此我想发表一个解释事情更简单的答案。我的答案将涵盖99%反复发布的错误。有关错误背后的实际原因,请查看已接受的答案。


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

通常情况下,当您为一个请求发送多个响应时,会出现以下错误消息:

Error: Can't set headers after they are sent.

确保每个请求仅调用以下功能一次:

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

(还有一些很少使用的功能,请查看已接受的答案)

当调用这些res函数时,路由回调不会返回。它将继续运行,直到达到函数的末尾或返回语句。如果要在发送响应时返回,可以这样做: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' });
}

当向/api/route1发送POST请求时,它将运行回调中的每一行。因为调用res.json()两次,即已发送两个响应,将引发一个Can't set headers after they are sent错误消息。

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


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

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())在代码示例中。如果没有错误(err),将调用findOneAndUpdate()。由于这个函数是异步的,res.json(doc1)将立即被调用。假设findOneAndUpdate()没有错误。然后将调用else中的res.json(doc2)。现在已发送了两个响应,因此会出现Can't set headers错误消息。

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

0
0 Comments

在Express中,res对象是Node.js的http.ServerResponse的子类 (可以阅读http.js源代码)。你可以随意调用res.setHeader(name, value),直到你调用res.writeHead(statusCode)。在调用writeHead之后,头部信息就已经确定了,你只能调用res.write(data),最后调用res.end(data)

错误信息"Error: Can't set headers after they are sent."的意思是你已经进入了Body或Finished状态,但某些函数仍然试图设置头部信息或statusCode。当你看到这个错误时,试着查找一下在已经输出了一些body后,有没有任何试图再发送头部信息的东西。例如,可以查找被不小心调用了两次的回调函数,或者在发送了body后发生的任何错误。

在你的情况中,你调用了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')

响应内容可以是头部和主体,当执行完毕后会变成已完成状态:

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

响应内容可以是头部和主体,且不会改变当前状态:

  1. res.addTrailers(headers)

响应内容必须只为头部,当执行完毕后会变成已完成状态:

  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)
0