我对 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);
我可以知道我的代码有什么问题吗?
Express 中的res
对象是 Node.js 的http.ServerResponse
的子类(请阅读 http.js 源代码)。允许您res.setHeader(name, value)
,直到调用res.writeHead(statusCode)
为止。在writeHead
之后,将烘焙标题,并且您只能调用res.write(data)
,最后res.end(data)
。
错误 “错误:发送标头后无法设置标头”。表示您已经处于 “正文” 或“完成” 状态,但是某些函数试图设置标头或 statusCode。当您看到此错误时,请尝试查找在某些正文已被写入之后尝试发送标头的任何内容。例如,查找意外调用两次的回调,或发送正文后发生的任何错误。
在您的情况下,您调用了res.redirect()
,这导致响应变为 “完成”。然后,您的代码引发了错误( res.req
为null
)。并且由于错误发生在您的实际function(req, res, next)
(而不是回调)中,因此 Connect 能够捕获该错误,然后尝试发送 500 错误页面。但是,由于已经发送了标头,因此 Node.js 的setHeader
引发了您所看到的错误。
回复必须在标题中,并保持在标题中:
res.writeContinue()
res.statusCode = 404
res.setHeader(name, value)
res.getHeader(name)
res.removeHeader(name)
res.header(key[, val])
(仅表达)res.charset = 'utf-8'
(仅 Express;仅影响 Express 特定的方法)res.contentType(type)
(仅表达)响应必须在头脑中,然后变成身体:
响应可以是头部 / 身体,也可以是身体:
响应可以是Head / Body ,也可以是 Finished :
响应可以在 “ Head / Body” 中,也可以保持其当前状态:
响应必须在标题中,并已完成:
return next([err])
(仅用于 Connect / Express)function(req, res, next)
(仅适用于 Connect / Express)res.send(body|status[, headers|status[, status]])
(仅表达)res.attachment(filename)
(仅表达)res.sendfile(path[, options[, callback]])
(仅表达)res.json(obj[, headers|status[, status]])
(仅表达)res.redirect(url[, status])
(仅表达)res.cookie(name, val[, options])
(仅表达)res.clearCookie(name[, options])
(仅表达)res.render(view[, options[, fn]])
(仅表达)res.partial(view[, options])
(仅表达)我也有一段时间遇到这个错误。我想(希望)我把头缠住了,想在这里写一下以供参考。
app.use
方法添加中间件以进行连接或表示(基于 connect 建立)时,您将Server.prototype.stack
各项附加到 Server.prototype.stack(至少使用当前的npm install connect
,它看起来与截至本文的一个 github)。服务器收到请求后,将遍历堆栈,并调用(request, response, next)
方法。
问题是,如果在其中一个中间件项中写入响应主体或标头(它看起来是 / 或某种原因),但没有调用response.end()
,而是调用next()
则作为核心Server.prototype.handle
方法完成后,将会注意到:
response.headerSent
为 true。因此,它引发一个错误。但是它引发的错误只是这个基本响应(来自 connect http.js
源代码:
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Cannot ' + req.method + ' ' + req.url);
就在那儿,它正在调用res.setHeader('Content-Type', 'text/plain');
,您很可能已在render
方法中设置了它,而无需调用 response.end() ,例如:
response.setHeader("Content-Type", "text/html");
response.write("<p>Hello World</p>");
一切需要结构化的方式是这样的:
// middleware that does not modify the response body
var doesNotModifyBody = function(request, response, next) {
request.params = {
a: "b"
};
// calls next because it hasn't modified the header
next();
};
// middleware that modify the response body
var doesModifyBody = function(request, response, next) {
response.setHeader("Content-Type", "text/html");
response.write("<p>Hello World</p>");
response.end();
// doesn't call next()
};
app.use(doesNotModifyBody);
app.use(doesModifyBody);
var problemMiddleware = function(request, response, next) {
response.setHeader("Content-Type", "text/html");
response.write("<p>Hello World</p>");
next();
};
response.end()
即可设置响应头,而无需调用next()
,这会使 connect 的服务器感到困惑。
此问答中的某些答案是错误的。接受的答案也不是很 “实用”,因此我想发布一个答案,以更简单的方式解释事情。我的答案将涵盖我一次又一次看到的错误中的 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 时,它将在回调中的每一行运行。 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()
如果没有错误( err
),则将调用findOneAndUpdate()
由于此函数是异步的,因此将立即调用res.json(doc1)
findOneAndUpdate()
中没有错误。然后将调用else
的res.json(doc2)
现在已发送两个响应,并且出现“无法设置标题”错误消息。
在这种情况下,解决方法是删除res.json(doc1)
。要将两个文档发送回客户端,可以将 else 中res.json()
res.json({ article: doc1, user: doc2 })
。