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


Je suis relativement nouveau à Node.js et j'ai quelques problèmes.

J'utilise Node.js 4.10 et Express 2.4.3.

Quand 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 avec mon code?


451
2017-08-12 15:21


origine


Réponses:


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

L'erreur "Erreur: Impossible de définir les en-têtes après leur envoi." signifie que vous êtes déjà dans l'état Corps ou Terminé, mais certaines fonctions ont tenté de définir un en-tête ou un StatusCode. Lorsque vous voyez cette erreur, essayez de rechercher tout ce qui essaie d'envoyer un en-tête après que le corps a déjà été écrit. Par exemple, recherchez les rappels accidentellement appelés deux fois ou toute erreur qui survient après l'envoi du corps.

Dans votre cas, vous avez appelé res.redirect(), ce qui a provoqué la fin de la réponse. Ensuite, votre code a généré une erreur (res.req est null). et depuis l'erreur s'est produite dans votre réel function(req, res, next) (pas dans un rappel), Connect était capable de l'attraper et a ensuite essayé d'envoyer une page d'erreur 500. Mais puisque les en-têtes ont déjà été envoyés, Node.js setHeader jeté l'erreur que vous avez vu.

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

La réponse doit être dans 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 seulement)
  7. res.charset = 'utf-8' (Express uniquement, affecte uniquement les méthodes spécifiques à Express)
  8. res.contentType(type) (Express seulement)

La réponse doit être dans 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 dans Tête et devient Fini:

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

745
2017-10-17 03:38



J'ai également rencontré cette erreur pendant un moment. Je pense (j'espère) que j'en ai enveloppé la tête, je voulais l'écrire ici pour référence.

Lorsque vous ajoutez un middleware à relier ou Express (qui est construit sur se connecter) en utilisant le app.use méthode, vous ajoutez des éléments à Server.prototype.stack en connect (Au moins avec le courant npm install connect, qui est assez différent du github de ce post). Lorsque le serveur reçoit une requête, il itère sur la pile, appelant le (request, response, next) méthode.

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

  1. il n'y a plus d'objets dans la pile, et / ou
  2. cette response.headerSent est vrai.

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

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

Juste là, c'est appeler res.setHeader('Content-Type', 'text/plain');, que vous êtes susceptible d'avoir mis dans votre render méthode, sans appeler response.end (), quelque chose comme:

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

La manière dont tout doit être structuré est la suivante:

Bon middleware

// 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 appels next(), ce qui perturbe le serveur de connexion.


72
2018-02-14 13:02



J'avais ce même problème et je me suis rendu compte que c'était parce que j'appelais res.redirect sans une déclaration de retour, donc la fonction suivante était également appelée immédiatement après:

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

Ce qui aurait dû être:

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

34
2017-08-12 18:24



Beaucoup de gens ont frappé cette erreur. C'est une confusion avec le traitement asynchrone. Il est probable qu'une partie de votre code définit les en-têtes dans la première case, puis vous exécutez un rappel asynchrone dans une prochaine case à cocher. Entre les deux, 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 car l'en-tête de réponse a déjà été transmis.

Je ne suis pas sûr exactement ce qui cause votre erreur, mais regardez les rappels comme domaines potentiels à étudier.

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

Voir aussi everyauth module, qui fait Facebook et une douzaine d'autres fournisseurs d'authentification tiers.


30
2017-07-04 05:22



J'ai bouilli la tête sur cette question et c'est arrivé à cause d'une erreur négligente sur la gestion des rappels. les rappels non retournés provoquent une double réponse.

Mon programme avait un code qui valide la requête et interroge la base de données. Après avoir validé si une erreur est présente, je rappelais le fichier index.js avec les erreurs de validation. Et si la validation passe, elle va de l'avant et frappe le db avec succès / échec.

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

Ce qui s’est passé, c’est que la validation d’incase échoue, le rappel est appelé et la réponse est définie. Mais pas retourné. Donc ça continue encore la méthode va à db et frappe succès / échec. Il appelle à nouveau le même rappel, ce qui entraîne le réglage de la réponse deux fois maintenant.

Donc, la solution est simple, vous devez "renvoyer" le rappel pour que la méthode ne continue pas à s'exécuter, une fois que l'erreur est survenue et donc définir l'objet de réponse une fois

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

13
2017-09-28 13:21



Ce type d'erreur est obtenu lorsque vous transmettez des instructions après l'envoi d'une réponse.

Par exemple:

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

Entraînera l'erreur que vous voyez, car une fois la réponse envoyée, les éléments suivants res.send ne sera pas exécuté.

Si vous voulez faire quelque chose, vous devriez le faire avant d'envoyer la réponse.


13
2018-01-06 01:10



Certaines des réponses à cette question sont erronées. La réponse acceptée n'est pas très "pratique", donc je veux poster une réponse qui explique les choses en termes plus simples. Ma réponse couvrira 99% des erreurs que je vois posté encore et encore. Pour les raisons réelles de l'erreur, jetez un coup d'œil à la réponse acceptée.


HTTP utilise un cycle qui nécessite une réponse par requête. Lorsque le client envoie une demande (par exemple, POST ou GET), le serveur ne doit lui envoyer qu'une seule réponse.

Ce message d'erreur:

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

se produit généralement lorsque vous envoyez plusieurs réponses pour une demande. Assurez-vous que les fonctions suivantes ne sont appelées qu'une seule fois par requête:

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

(et quelques autres qui sont rarement utilisés, vérifiez la réponse acceptée)

Le rappel de route ne retournera pas lorsque ces fonctions res sont appelées. Il continuera à fonctionner jusqu'à ce qu'il atteigne la fin de la fonction ou une déclaration de retour. Si vous voulez revenir en envoyant une réponse, vous pouvez le faire comme ceci: return res.send().


Prenez par exemple ce code:

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' });
}

Lorsqu'une demande POST est envoyée à / api / route1 il exécutera chaque ligne dans le rappel. UNE Impossible de définir les en-têtes après leur envoi message d'erreur sera lancé parce que res.json() est appelé deux fois, ce qui signifie que deux réponses sont envoyées.

Une seule réponse peut être envoyée par demande!


L'erreur dans l'exemple de code ci-dessus était évidente. Un problème plus typique est lorsque vous avez plusieurs branches:

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.
    });
}

Cet itinéraire avec rappel associé trouve une entreprise dans une base de données. Lorsque vous faites une requête pour une entreprise qui n'existe pas, nous allons entrer dans le else if branchez et envoyez une réponse 404. Après cela, nous allons continuer à la déclaration suivante qui envoie également une réponse. Nous avons maintenant envoyé deux réponses et le message d'erreur apparaîtra. Nous pouvons corriger ce code en nous assurant que nous envoyons seulement une réponse:

.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);
  }
});

ou en revenant lorsque la réponse est envoyée:

.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);
});

Un grand pécheur est des fonctions asynchrones. Prenez la fonction de ce question, par exemple:

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.
  }
});

Ici, nous avons une fonction asynchrone (findOneAndUpdate()) dans l'exemple de code. S'il n'y a pas d'erreurs (err) findOneAndUpdate() sera appelé. Parce que cette fonction est asynchrone res.json(doc1) sera appelé immédiatement. Supposons qu'il n'y a pas d'erreurs dans findOneAndUpdate(). le res.json(doc2) dans le else sera alors appelé. Deux réponses ont été envoyées et le Impossible de définir les en-têtes message d'erreur se produit

La solution, dans ce cas, serait d'enlever le res.json(doc1). Pour renvoyer les deux documents au client, res.json() dans l'autre pourrait être écrit comme res.json({ article: doc1, user: doc2 }).


9
2018-05-17 00:05