1234 Stimmen

Fehler: Kopfzeilen können nicht gesetzt werden, nachdem sie an den Client gesendet wurden

Ich bin ziemlich neu in Node.js und ich bin mit einigen Fragen.

Ich verwende Node.js 4.10 und Express 2.4.3.

Wenn ich versuche, auf http://127.0.0.1:8888/auth/facebook werde ich weitergeleitet zu http://127.0.0.1:8888/auth/facebook_callback .

Daraufhin erhielt ich die folgende Fehlermeldung:

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)

Der folgende Code lautet wie folgt:

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

Darf ich erfahren, was an meinem Code falsch ist?

1643voto

yonran Punkte 16833

El res Objekt in Express ist eine Unterklasse von Node.js's http.ServerResponse ( den http.js-Quelltext lesen ). Sie dürfen anrufen res.setHeader(name, value) so oft Sie wollen, bis Sie anrufen res.writeHead(statusCode) . Nach writeHead sind die Kopfzeilen eingebaut, und Sie können nur res.write(data) und schließlich res.end(data) .

Der Fehler "Error: Can't set headers after they are sent." bedeutet, dass Sie sich bereits im Status Body oder Finished befinden, aber eine Funktion versucht hat, einen Header oder StatusCode zu setzen. Wenn Sie diesen Fehler sehen, suchen Sie nach allem, was versucht, einen Header zu senden, nachdem ein Teil des Bodys bereits geschrieben wurde. Suchen Sie zum Beispiel nach Callbacks, die versehentlich zweimal aufgerufen werden, oder nach Fehlern, die auftreten, nachdem der Body gesendet wurde.

In Ihrem Fall haben Sie res.redirect() was dazu führte, dass die Reaktion beendet wurde. Dann gab Ihr Code einen Fehler aus ( res.req est null ). Und da der Fehler innerhalb Ihrer aktuellen function(req, res, next) (nicht innerhalb eines Rückrufs), konnte Connect es abfangen und versuchte dann, eine 500-Fehlerseite zu senden. Aber da die Header bereits gesendet wurden, konnte Node.js's setHeader hat den Fehler ausgelöst, den Sie gesehen haben.

Umfassende Liste der Antwortmethoden von Node.js/Express und wann sie aufgerufen werden müssen:

Die Antwort muss in Kopf und bleibt in Kopf :

  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]) (nur Express)
  7. res.charset = 'utf-8' (nur Express; betrifft nur Express-spezifische Methoden)
  8. res.contentType(type) (nur Express)

Die Antwort muss in Kopf und wird zu Körper :

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

Die Antwort kann entweder in Kopf/Körper und bleibt in Körper :

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

Die Antwort kann entweder in Kopf/Körper und wird zu Fertige :

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

Die Antwort kann entweder in Kopf/Körper und bleibt in seinem derzeitigen Zustand:

  1. res.addTrailers(headers)

Die Antwort muss in Kopf und wird zu Fertige :

  1. return next([err]) (nur Connect/Express)
  2. Alle Ausnahmen innerhalb der Middleware function(req, res, next) (nur Connect/Express)
  3. res.send(body|status[, headers|status[, status]]) (nur Express)
  4. res.attachment(filename) (nur Express)
  5. res.sendfile(path[, options[, callback]]) (nur Express)
  6. res.json(obj[, headers|status[, status]]) (nur Express)
  7. res.redirect(url[, status]) (nur Express)
  8. res.cookie(name, val[, options]) (nur Express)
  9. res.clearCookie(name[, options]) (nur Express)
  10. res.render(view[, options[, fn]]) (nur Express)
  11. [res.partial(view[, options])](http://expressjs.com/2x/guide.html#res.partial()) (nur Express)

248voto

Mika Sundland Punkte 16462

Einige der Antworten in dieser Frage und Antwort sind falsch. Die akzeptierte Antwort ist auch nicht sehr "praktisch", daher möchte ich eine Antwort veröffentlichen, die die Dinge in einfacheren Begriffen erklärt. Meine Antwort deckt 99 % der Fehler ab, die immer wieder gepostet werden. Die eigentlichen Gründe für den Fehler finden Sie in der akzeptierten Antwort.


HTTP verwendet einen Zyklus, der eine Antwort pro Anfrage erfordert. Wenn der Client eine Anfrage sendet (z.B. POST oder GET), sollte der Server nur eine Antwort zurückschicken.

Diese Fehlermeldung:

Fehler: Kopfzeilen können nicht gesetzt werden, nachdem sie gesendet wurden.

geschieht in der Regel, wenn Sie mehrere Antworten auf eine Anfrage senden. Stellen Sie sicher, dass die folgenden Funktionen nur einmal pro Anfrage aufgerufen werden:

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

<em>(und ein paar weitere, die selten verwendet werden, siehe die akzeptierte Antwort)</em>

Der Routen-Callback kehrt nicht zurück, wenn diese res-Funktionen aufgerufen werden. Er läuft weiter, bis er das Ende der Funktion oder eine Return-Anweisung erreicht. Wenn Sie beim Senden einer Antwort zurückkehren möchten, können Sie dies wie folgt tun: return res.send() .


Nehmen Sie zum Beispiel diesen 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' });
}

Wenn eine POST-Anfrage an /api/route1 wird jede Zeile des Rückrufs ausgeführt. A Kopfzeilen können nicht gesetzt werden, nachdem sie gesendet wurden wird eine Fehlermeldung ausgelöst, weil res.json() wird zweimal aufgerufen, d. h. es werden zwei Antworten gesendet.

Pro Anfrage kann nur eine Antwort gesendet werden!


Der Fehler im obigen Codebeispiel war offensichtlich. Ein typischeres Problem tritt auf, wenn Sie mehrere Verzweigungen haben:

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

Diese Route mit angehängtem Callback findet ein Unternehmen in einer Datenbank. Bei einer Abfrage nach einer Firma, die nicht existiert, gelangen wir in die else if Zweig und senden eine 404-Antwort. Danach fahren wir mit der nächsten Anweisung fort, die ebenfalls eine Antwort sendet. Jetzt haben wir zwei Antworten gesendet, und die Fehlermeldung wird angezeigt. Wir können diesen Code korrigieren, indem wir sicherstellen, dass wir nur eine Antwort senden:

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

oder durch Rückgabe, wenn die Antwort gesendet wird:

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

Ein großer Sünder sind asynchrone Funktionen. Nehmen Sie die Funktion von este Frage, zum Beispiel:

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

Hier haben wir eine asynchrone Funktion ( findOneAndUpdate() ) in dem Codebeispiel. Wenn es keine Fehler gibt ( err ) findOneAndUpdate() aufgerufen werden. Da diese Funktion asynchron ist, wird die res.json(doc1) wird sofort aufgerufen. Angenommen, es gibt keine Fehler in findOneAndUpdate() . Die res.json(doc2) im else wird dann aufgerufen. Inzwischen wurden zwei Antworten verschickt und die Kopfzeilen können nicht gesetzt werden Fehlermeldung auftritt.

Die Lösung wäre in diesem Fall, die res.json(doc1) . Um beide Dokumente an den Kunden zurückzuschicken, muss der res.json() in der anderen könnte geschrieben werden als res.json({ article: doc1, user: doc2 }) .

162voto

Lance Punkte 69299

Dieser Fehler trat auch bei mir eine Zeit lang auf. Ich denke (Hoffnung) Ich habe meinen Kopf um es gewickelt, wollte es hier als Referenz zu schreiben.

Wenn Sie Middleware zu verbinden o express (das auf connect aufbaut) unter Verwendung der app.use Methode fügen Sie Elemente an die Server.prototype.stack in connect (Zumindest mit dem aktuellen npm install connect die sich von der Github-Version dieses Beitrags deutlich unterscheidet). Wenn der Server eine Anfrage erhält, durchläuft er den Stack und ruft die (request, response, next) Methode.

Das Problem ist, wenn in einer der Middleware-Elemente schreibt, um die Antwort Körper oder Header (es sieht aus wie es entweder/oder aus irgendeinem Grund), ruft aber nicht response.end() und Sie rufen next() dann als Kernstück Server.prototype.handle Methode abgeschlossen ist, wird sie das bemerken:

  1. sich keine Gegenstände mehr im Stapel befinden, und/oder
  2. que response.headerSent wahr ist.

Es wird also ein Fehler ausgegeben. Aber der Fehler, den es auslöst, ist nur diese einfache Antwort (aus dem connect http.js Quellcode:

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

Genau da, es ruft res.setHeader('Content-Type', 'text/plain'); die Sie wahrscheinlich in Ihrer Datenbank eingestellt haben. render Methode, ohne den Aufruf von response.end() etwa so:

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

So muss alles strukturiert werden:

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

Problematische Middleware

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

Die problematische Middleware setzt den Antwort-Header ohne Aufruf von response.end() und ruft next() was den Server von connect verwirrt.

74voto

ergusto Punkte 1167

Ich hatte das gleiche Problem und erkannte, dass es daran lag, dass ich die res.redirect ohne eine return Anweisung, so dass die next Funktion wurde ebenfalls unmittelbar danach aufgerufen:

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

Was eigentlich hätte sein müssen:

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

54voto

Peter Lyons Punkte 137811

Dieser Fehler tritt bei vielen Menschen auf. Es ist eine Verwechslung mit der asynchronen Verarbeitung. Höchstwahrscheinlich setzt ein Teil Ihres Codes Header im ersten Tick und dann führen Sie einen asynchronen Callback in einem zukünftigen Tick aus. Dazwischen wird der Response-Header gesendet, aber dann versuchen weitere Header (wie eine 30-fache Weiterleitung), zusätzliche Header hinzuzufügen, aber es ist zu spät, da der Response-Header bereits gesendet wurde.

Ich bin mir nicht sicher, was genau Ihren Fehler verursacht, aber schauen Sie sich alle Rückrufe als mögliche Bereiche zu untersuchen.

Ein einfacher Tipp zur Vereinfachung Ihres Codes. Werden Sie los app.configure() und rufen Sie einfach app.use direkt in Ihrem Top-Level-Bereich.

Siehe auch die Jederauth Modul, das Facebook und etwa ein Dutzend anderer Anbieter von Authentifizierungsdiensten unterstützt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X