Error: Can't set headers after they are sent to the client

来源:互联网 发布:淘宝客平台源码 编辑:程序博客网 时间:2024/05/22 16:56

I'm fairly new to Node.js and I am having some issues.

I am using Node.js 4.10 and Express 2.4.3.

When I try to access http://127.0.0.1:8888/auth/facebook, i'll be redirected to http://127.0.0.1:8888/auth/facebook_callback.

I than received the following error:

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:9node.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)

The following is my code:

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 securityvar 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);

May I know what is wrong with my code?

I'm really new to this, so sorry for just putting up the code here.

Thank you all in advance.




The res object in Express is a subclass of Node.js's http.ServerResponse (read the http.js source). You are allowed to call res.setHeader(name, value) as often as you want until you call res.writeHead(statusCode). After writeHead, the headers are baked in and you can only call res.write(data), and finally res.end(data).

The error "Error: Can't set headers after they are sent." means that you're already in the Body or Finished state, but some function tried to set a header or statusCode. When you see this error, try to look for anything that tries to send a header after some of the body has already been written. For example, look for callbacks that are accidentally called twice, or any error that happens after the body is sent.

In your case, you called res.redirect(), which caused the response to become Finished. Then your code threw an error (res.req is null). and since the error happened within your actual function(req, res, next) (not within a callback), Connect was able to catch it and then tried to send a 500 error page. But since the headers were already sent, Node.js's setHeader threw the error that you saw.

Comprehensive list of Node.js/Express response methods and when they must be called:

Response must be in Head and remains in 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 only)
  7. res.charset = 'utf-8' (Express only; only affects Express-specific methods)
  8. res.contentType(type) (Express only)

Response must be in Head and becomes Body:

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

Response can be in either Head/Body and remains in Body:

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

Response can be in either Head/Body and becomes Finished:

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

Response can be in either Head/Body and remains in its current state:

  1. res.addTrailers(headers)

Response must be in Head and becomes Finished:

  1. return next([err]) (Connect/Express only)
  2. Any exceptions within middleware function(req, res, next) (Connect/Express only)
  3. res.send(body|status[, headers|status[, status]]) (Express only)
  4. res.attachment(filename) (Express only)
  5. res.sendfile(path[, options[, callback]]) (Express only)
  6. res.json(obj[, headers|status[, status]]) (Express only)
  7. res.redirect(url[, status]) (Express only)
  8. res.cookie(name, val[, options]) (Express only)
  9. res.clearCookie(name[, options]) (Express only)
  10. res.render(view[, options[, fn]]) (Express only)
  11. res.partial(view[, options]) (Express only)

I ran into this error as well for a while. I think (hope) I've wrapped my head around it, wanted to write it here for reference.

When you add middleware to connect or express (which is built on connect) using the app.usemethod, you're appending items to Server.prototype.stack in connect (At least with the current npm install connect, which looks quite different from the one github as of this post). When the server gets a request, it iterates over the stack, calling the (request, response, next) method.

The problem is, if in one of the middleware items writes to the response body or headers (it looks like it's either/or for some reason), but doesn't call response.end() and you call next() then as the core Server.prototype.handle method completes, it's going to notice that:

  1. there are no more items in the stack, and/or
  2. that response.headerSent is true.

So, it throws an error. But the error it throws is just this basic response (from the connect http.jssource code:

res.statusCode = 404;res.setHeader('Content-Type', 'text/plain');res.end('Cannot ' + req.method + ' ' + req.url);

Right there, it's calling res.setHeader('Content-Type', 'text/plain');, which you are likely to have set in your render method, without calling response.end(), something like:

response.setHeader("Content-Type", "text/html");response.write("<p>Hello World</p>");

The way everything needs to be structured is like this:

Good Middleware

// middleware that does not modify the response bodyvar doesNotModifyBody = function(request, response, next) {  request.params = {    a: "b"  };  // calls next because it hasn't modified the header  next();};// middleware that modify the response bodyvar 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);

Problematic Middleware

var problemMiddleware = function(request, response, next) {  response.setHeader("Content-Type", "text/html");  response.write("<p>Hello World</p>");  next();};

The problematic middleware sets the response header without calling response.end() and calls next(), which confuses connect's server.



25down vote

I had this same issue and realised it was because I was calling res.redirect without a return statement, so the next function was also being called immediately afterwards:

auth.annonymousOnly = function(req, res, next) {    if (req.user) res.redirect('/');    next();};

Which should have been:

auth.annonymousOnly = function(req, res, next) {    if (req.user) return res.redirect('/');    next();};
25down vote

I had this same issue and realised it was because I was calling res.redirect without a return statement, so the next function was also being called immediately afterwards:

auth.annonymousOnly = function(req, res, next) {    if (req.user) res.redirect('/');    next();};

Which should have been:

auth.annonymousOnly = function(req, res, next) {    if (req.user) return res.redirect('/');    next();};

I boiled my head over this issue and it has happened due to a careless mistake on handling the callbacks. non returned callbacks cause the response to be set twice.!

My program had a code which validate request and query the DB. after validating if error is there, I was calling back the index.js with the validation errors . And if validation passes it goes ahead and hit the db with success/failure.

    var error = validateRequestDetails("create",queryReq);    if (error)        callback(error, null);   else    some code     callback(null, success);

What was happening is : Incase validation fails the callback get called and response get set. But not returned. So it still continues the method goes to db and hit success/failure . It calls the same callback again causing the response to be set twice now.

So solution is simple, you need to 'return' the callback so that the method don't continue executing, once the error has occurred and hence set the response object once

  var error = validateRequestDetails("create",queryReq);    if (error)        callback(error, null);        return;    else       some code        callback(null, success);

25down vote

I had this same issue and realised it was because I was calling res.redirect without a return statement, so the next function was also being called immediately afterwards:

auth.annonymousOnly = function(req, res, next) {    if (req.user) res.redirect('/');    next();};

Which should have been:

auth.annonymousOnly = function(req, res, next) {    if (req.user) return res.redirect('/');    next();};

This type of error you will get when you pass statements after sending a response.

For example:

res.send("something response");console.log("jhgfjhgsdhgfsdf");console.log("sdgsdfhdgfdhgsdf");res.send("sopmething response");

Will result in the error you are seeing, because once the response has been sent, the following res.send will not be executed.

If you want do anything, you should do it before sending the response.

25down vote

I had this same issue and realised it was because I was calling res.redirect without a return statement, so the next function was also being called immediately afterwards:

auth.annonymousOnly = function(req, res, next) {    if (req.user) res.redirect('/');    next();};

Which should have been:

auth.annonymousOnly = function(req, res, next) {    if (req.user) return res.redirect('/');    next();};

In my case it was a 304 response (caching) that was causing the issue.

Easiest solution:

app.disable('etag');

Alternate solution here if you want more control:

http://vlasenko.org/2011/10/12/expressconnect-static-set-last-modified-to-now-to-avoid-304-not-modified/

25down vote

I had this same issue and realised it was because I was calling res.redirect without a return statement, so the next function was also being called immediately afterwards:

auth.annonymousOnly = function(req, res, next) {    if (req.user) res.redirect('/');    next();};

Which should have been:

auth.annonymousOnly = function(req, res, next) {    if (req.user) return res.redirect('/');    next();};

https://stackoverflow.com/questions/7042340/error-cant-set-headers-after-they-are-sent-to-the-client/7086621#7086621
25down vote

I had this same issue and realised it was because I was calling res.redirect without a return statement, so the next function was also being called immediately afterwards:

auth.annonymousOnly = function(req, res, next) {    if (req.user) res.redirect('/');    next();};

Which should have been:

auth.annonymousOnly = function(req, res, next) {    if (req.user) return res.redirect('/');    next();};
阅读全文
0 0
原创粉丝点击