927 votes

Erreur : Impossible de définir les en-têtes après leur envoi au client

Je suis assez nouveau dans Node.js et j'ai quelques problèmes.

J'utilise Node.js 4.10 et Express 2.4.3.

Lorsque j'essaie d'accéder http://127.0.0.1:8888/auth/facebook je serai redirigé vers http://127.0.0.1:8888/auth/facebook_callback .

J'ai ensuite reçu l'erreur suivante :

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)

Voici mon 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 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);

Puis-je savoir ce qui ne va pas dans mon code ?

0 votes

Réponse simple de visionmedia : github.com/visionmedia/express/issues/634

4 votes

Google m'a envoyé à cette question, mais les nouvelles versions d'ExpressJS ont res.headersSent booléen qui peut être utilisé pour vérifier si l'envoi d'en-têtes est sûr.

1333voto

yonran Points 6952

El res dans Express est une sous-classe de Node.js http.ServerResponse ( lire le source http.js ). Vous êtes autorisé à appeler res.setHeader(name, value) aussi souvent que vous le souhaitez jusqu'à ce que vous appeliez res.writeHead(statusCode) . Après writeHead les en-têtes sont intégrés et vous pouvez seulement appeler res.write(data) et enfin res.end(data) .

L'erreur "Error : Can't set headers after they are sent" signifie que vous êtes déjà dans l'état Body ou Finished, mais qu'une fonction a essayé de définir un en-tête ou un statusCode. Lorsque vous voyez cette erreur, essayez de rechercher tout ce qui tente d'envoyer un en-tête après qu'une partie du corps ait déjà été écrite. Par exemple, recherchez les callbacks qui sont accidentellement appelés deux fois, ou toute erreur qui se produit après l'envoi du corps.

Dans votre cas, vous avez appelé res.redirect() ce qui a fait que la réponse est devenue Finished. Ensuite, votre code a lancé une erreur ( res.req es null ). Et comme l'erreur s'est produite dans le cadre de votre function(req, res, next) (pas dans un callback), Connect a pu l'attraper et a ensuite essayé d'envoyer une page d'erreur 500. Mais comme les en-têtes ont déjà été envoyés, la fonction de Node.js setHeader a lancé l'erreur que vous avez vue.

Liste complète des méthodes de réponse Node.js/Express et quand elles doivent être appelées :

La réponse doit être en Tête et reste dans Tête :

  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 uniquement)
  7. res.charset = 'utf-8' (Express uniquement ; n'affecte que les méthodes spécifiques à Express)
  8. res.contentType(type) (Express uniquement)

La réponse doit être en Tête et devient Corps :

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

La réponse peut être soit Tête/Corps et reste dans Corps :

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

La réponse peut être soit Tête/Corps et devient Fini :

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

La réponse peut être soit Tête/Corps et reste dans son état actuel :

  1. res.addTrailers(headers)

La réponse doit être en Tête et devient Fini :

  1. return next([err]) (Connect/Express uniquement)
  2. Toute exception au sein de l'intergiciel function(req, res, next) (Connect/Express uniquement)
  3. res.send(body|status[, headers|status[, status]]) (Express uniquement)
  4. res.attachment(filename) (Express uniquement)
  5. res.sendfile(path[, options[, callback]]) (Express uniquement)
  6. res.json(obj[, headers|status[, status]]) (Express uniquement)
  7. res.redirect(url[, status]) (Express uniquement)
  8. res.cookie(name, val[, options]) (Express uniquement)
  9. res.clearCookie(name[, options]) (Express uniquement)
  10. res.render(view[, options[, fn]]) (Express uniquement)
  11. [res.partial(view[, options])](http://expressjs.com/2x/guide.html#res.partial()) (Express uniquement)

41 votes

Oui, vérifiez si vous appelez next() ou un autre cb deux fois.

3 votes

Les liens express semblent morts

40 votes

Faites également attention à cette erreur classique : res.redirect() n'arrête pas l'exécution de l'instruction... donc retournez après elle. Sinon, un autre code pourrait être exécuté et provoquer involontairement la fameuse erreur d'en-tête. Merci pour l'explication !

137voto

Lance Pollard Points 17503

J'ai également rencontré cette erreur pendant un certain temps. Je pense (j'espère) l'avoir résolu, mais je voulais l'écrire ici pour référence.

Lorsque vous ajoutez un intergiciel à connecter o express (qui est construit sur connect) en utilisant le app.use vous ajoutez des éléments à Server.prototype.stack en connexion (Du moins avec l'actuel npm install connect (qui est très différent de celui de github à la date de cet article). Lorsque le serveur reçoit une requête, il itère sur la pile, en appelant la fonction (request, response, next) método.

Le problème est que si l'un des éléments du middleware écrit dans le corps de la réponse ou dans les en-têtes (il semble que ce soit l'un ou l'autre pour une raison quelconque), mais n'appelle pas response.end() et vous appelez next() alors que le noyau Server.prototype.handle se termine, il va le remarquer :

  1. il n'y a plus d'éléments dans la pile, et/ou
  2. que response.headerSent est vrai.

Donc, il jette une erreur. Mais l'erreur qu'il jette est juste cette réponse de base (de la connexion http.js code source :

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

Juste là, c'est l'appel res.setHeader('Content-Type', 'text/plain'); que vous avez probablement défini dans votre système de gestion de l'information. render méthode, sans appeler response.end() quelque chose comme :

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

La façon dont tout doit être structuré est la suivante :

Un bon intergiciel

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

Middleware problématique

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

Le middleware problématique définit l'en-tête de réponse sans appeler response.end() et appelle next() ce qui perturbe le serveur de Connect.

8 votes

+1 C'est une excellente explication, mais qu'en est-il du cas où vous utilisez res.redirect() ? Je rencontre fréquemment ce problème lorsque l'intergiciel tente de rediriger en fonction de certaines conditions. Le middleware ne devrait-il pas rediriger, selon votre exemple de "bon middleware" ?

0 votes

Vous savez que j'ai exactement ce problème à cause de ce que vous appelez un middleware problématique, cependant j'ai besoin d'un cas où je renvoie la réponse mais je voudrais faire un traitement supplémentaire dans un contrôleur séparé comme partie de la chaîne, comment puis-je supprimer cette erreur ?

46voto

Peter Lyons Points 47794

Beaucoup de personnes rencontrent cette erreur. C'est une confusion avec le traitement asynchrone. Il est fort probable qu'une partie de votre code définisse les en-têtes dans le premier tick, puis exécute un callback asynchrone dans un tick ultérieur. Entre-temps, l'en-tête de réponse est envoyé, mais d'autres en-têtes (comme une redirection 30X) tentent d'ajouter des en-têtes supplémentaires, mais il est trop tard puisque l'en-tête de réponse a déjà été transmis.

Je ne sais pas exactement ce qui cause votre erreur, mais regardez les rappels comme des zones potentielles à examiner.

Un conseil simple pour simplifier votre code. Débarrassez-vous de app.configure() et appeler simplement app.use directement dans votre portée de niveau supérieur.

Voir également le everyauth qui fait Facebook et une douzaine d'autres fournisseurs d'authentification tiers.

0 votes

Une redirection 30X est un code de réponse HTTP. w3.org/Protocoles/rfc2616/rfc2616-sec10.html Les codes 300 à 399 sont des variantes de la redirection, les codes 302 et 301 étant généralement utilisés pour envoyer le client vers une autre URL. Lorsque vous faites response.redirect(...) dans node, un en-tête de redirection 30X sera envoyé dans la réponse.

3 votes

Ohhhh. J'imaginais 30 redirections à la suite ou quelque chose comme ça.

5voto

blented Points 187

Dans mon cas, c'est une réponse 304 (mise en cache) qui était à l'origine du problème.

La solution la plus simple :

app.disable('etag');

Une autre solution ici si vous voulez plus de contrôle :

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

0 votes

Dans mon cas, il y a aussi 304 réponses. J'utilise Fibers pour le traitement. Votre réponse m'aide beaucoup. Merci.

0 votes

Quelqu'un peut-il expliquer quelles sont les implications de la suppression de l'en-tête etag ?

5 votes

Les balises ET permettent au serveur de ne pas envoyer un contenu qui n'a pas changé. Le fait de le désactiver désactive cette fonction. L'entrée ETag de wikipedia ( fr.wikipedia.org/wiki/HTTP_ETag ) a une explication plus longue.

0voto

shi11i Points 481

Réponse simple de visionmedia : https://github.com/visionmedia/express/issues/634

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X