Błąd: nie można ustawić nagłówków po wysłaniu do klienta

724

Jestem dość nowy w Node.js i mam pewne problemy.

Używam Node.js 4.10 i Express 2.4.3.

Kiedy próbuję uzyskać dostęp do http://127.0.0.1:8888/auth/facebook , nastąpi przekierowanie do http://127.0.0.1:8888/auth/facebook_callback .

Następnie otrzymałem następujący błąd:

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)

Oto mój kod:

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

Czy mogę wiedzieć, co jest nie tak z moim kodem?

DjangoRocks
źródło
prosta odpowiedź od visionmedia: github.com/visionmedia/express/issues/634
shi11i 30.11.11
2
Google wysłało mnie na to pytanie, ale nowsze wersje ExpressJS mają wartość logiczną res.headersSent, której można użyć do sprawdzenia, czy można bezpiecznie ustawić / wysłać nagłówki
Julian Soro

Odpowiedzi:

1112

resObiekt w Express jest podklasą node.js użytkownikahttp.ServerResponse ( czytaj źródła http.js ). Możesz dzwonić res.setHeader(name, value)tak często, jak chcesz, dopóki nie zadzwonisz res.writeHead(statusCode). Następnie writeHeadnagłówki są upieczone i możesz tylko zadzwonić res.write(data), i na koniec res.end(data).

Błąd „Błąd: nie można ustawić nagłówków po ich wysłaniu”. oznacza, że ​​jesteś już w stanie Body lub Finished, ale jakaś funkcja próbowała ustawić nagłówek lub kod statusu. Gdy zobaczysz ten błąd, spróbuj poszukać czegoś, co spróbuje wysłać nagłówek po napisaniu części treści. Na przykład poszukaj oddzwaniania, które są przypadkowo wywoływane dwukrotnie lub każdego błędu, który pojawia się po wysłaniu treści.

W twoim przypadku zadzwoniłeś res.redirect(), co spowodowało zakończenie odpowiedzi. Następnie twój kod zgłosił błąd ( res.reqjest null). a ponieważ błąd wystąpił w rzeczywistym function(req, res, next)(nie w ramach wywołania zwrotnego), Connect był w stanie go złapać, a następnie próbował wysłać stronę błędu 500. Ale ponieważ nagłówki zostały już wysłane, Node.js zgłosił setHeaderbłąd, który zobaczyłeś.

Wyczerpująca lista metod odpowiedzi Node.js / Express i kiedy należy je wywołać:

Odpowiedź musi być w głowie i pozostaje w głowie :

  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]) (Tylko ekspres)
  7. res.charset = 'utf-8' (Tylko Express; dotyczy tylko metod specyficznych dla Express)
  8. res.contentType(type) (Tylko ekspres)

Odpowiedź musi być w głowie i staje się Ciałem :

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

Odpowiedź może być w Głowie / Ciele i pozostaje w Ciele :

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

Odpowiedź może być w dowolnej Głowie / Ciele i kończy się :

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

Odpowiedź może być w dowolnej głowie / ciele i pozostaje w obecnym stanie:

  1. res.addTrailers(headers)

Odpowiedź musi być w głowie i kończy się :

  1. return next([err]) (Tylko Connect / Express)
  2. Wszelkie wyjątki w oprogramowaniu pośrednim function(req, res, next)(tylko Connect / Express)
  3. res.send(body|status[, headers|status[, status]]) (Tylko ekspres)
  4. res.attachment(filename) (Tylko ekspres)
  5. res.sendfile(path[, options[, callback]]) (Tylko ekspres)
  6. res.json(obj[, headers|status[, status]]) (Tylko ekspres)
  7. res.redirect(url[, status]) (Tylko ekspres)
  8. res.cookie(name, val[, options]) (Tylko ekspres)
  9. res.clearCookie(name[, options]) (Tylko ekspres)
  10. res.render(view[, options[, fn]]) (Tylko ekspres)
  11. res.partial(view[, options]) (Tylko ekspres)
yonran
źródło
13
Tak, sprawdź wywołanie dwa razy next () lub innego cb.
Tony Gutierrez
3
Łącza ekspresowe wydają się martwe
Korhan Ozturk,
25
również uważaj na ten klasyczny błąd: res.redirect () nie zatrzymuje wykonywania instrukcji ... więc wróć po nim. W przeciwnym razie mógłby zostać wykonany inny kod, który mógłby przypadkowo spowodować słynny błąd nagłówka. Dzięki za wyjaśnienie!
KLoozen,
Zazwyczaj dobrym pomysłem jest użycie return na końcu połączenia zwrotnego, aby tego uniknąć
thethakuri
4
Zrobiłem bardzo mały błąd w moim oprogramowaniu pośrednim, returnwcześniej next()tego nie robiłem , dzięki temu wskazałem na błąd!
illcrx
113

Przez jakiś czas również napotkałem ten błąd. Myślę, że (mam nadzieję) owinąłem głowę wokół, chciałem napisać to tutaj w celach informacyjnych.

Kiedy dodajesz oprogramowanie pośrednie do łączenia lub ekspresowe (które jest zbudowane na połączeniu) przy użyciu tej app.usemetody, dodajesz elementy do Server.prototype.stackw connect (Przynajmniej z bieżącym npm install connect, który wygląda zupełnie inaczej niż jeden github w tym poście). Gdy serwer otrzyma żądanie, iteruje stos, wywołując (request, response, next)metodę.

Problem polega na tym, że jeśli jeden z elementów oprogramowania pośredniego zapisuje w treści odpowiedzi lub nagłówkach (wygląda na to, że jest to albo / lub z jakiegoś powodu), ale nie wywołuje response.end()i wywołujesznext() wtedy, gdy Server.prototype.handlezakończy się podstawowa metoda, zauważy to że:

  1. na stosie nie ma już więcej przedmiotów i / lub
  2. że response.headerSentjest to prawda.

Zgłasza więc błąd. Ale błąd, który zgłasza, to tylko ta podstawowa odpowiedź (z http.jskodu źródłowego Connect :

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

W tym miejscu jest to wywołanie res.setHeader('Content-Type', 'text/plain');, które prawdopodobnie ustawiłeś w swojej rendermetodzie, bez wywoływania response.end () , coś takiego:

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

Sposób, w jaki wszystko musi być zorganizowane, wygląda następująco:

Dobre oprogramowanie pośrednie

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

Problematyczne oprogramowanie pośrednie

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

Problematyczne oprogramowanie pośrednie ustawia nagłówek odpowiedzi bez wywoływania response.end()i połączeń next(), co dezorientuje serwer Connect.

Lance Pollard
źródło
7
+1 To świetne wytłumaczenie, ale co z przypadkiem, gdy używasz res.redirect ()? Często napotykam ten problem, gdy oprogramowanie pośrednie próbuje przekierować na podstawie pewnych warunków. Czy oprogramowanie pośrednie nie powinno przekierowywać, zgodnie z przykładem „dobrego oprogramowania pośredniego”?
qodeninja,
Wiesz, że mam dokładnie ten problem ze względu na to, co nazywasz problematycznym oprogramowaniem pośrednim, ale potrzebuję przypadku, w którym zwracam odpowiedź, ale chciałbym wykonać dalsze przetwarzanie w oddzielnym kontrolerze jako część łańcucha, jak mam zająć się eliminowaniem tego błędu ?
iQ.
57

Niektóre odpowiedzi w tym pytaniu są nieprawidłowe. Przyjęta odpowiedź również nie jest zbyt „praktyczna”, dlatego chcę opublikować odpowiedź, która wyjaśnia rzeczy w prostszy sposób. Moja odpowiedź obejmie 99% błędów, które widzę opublikowanych w kółko. Z rzeczywistych przyczyn błędu spójrz na zaakceptowaną odpowiedź.


HTTP wykorzystuje cykl, który wymaga jednej odpowiedzi na żądanie. Gdy klient wysyła żądanie (np. POST lub GET), serwer powinien odesłać tylko jedną odpowiedź z powrotem.

Ten komunikat o błędzie:

Błąd: nie można ustawić nagłówków po ich wysłaniu.

zwykle dzieje się, gdy wysyłasz kilka odpowiedzi na jedno żądanie. Upewnij się, że następujące funkcje są wywoływane tylko raz na żądanie:

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

(i kilka innych, które są rzadko używane, sprawdź zaakceptowaną odpowiedź)

Oddzwanianie do trasy nie zostanie zwrócone po wywołaniu tych funkcji res. Będzie działał, dopóki nie osiągnie końca funkcji lub instrukcji return. Jeśli chcesz powrócić przy wysyłaniu odpowiedzi można zrobić to tak: return res.send().


Weźmy na przykład ten kod:

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

Gdy żądanie POST zostanie wysłane do / api / route1 , uruchomi każdą linię w wywołaniu zwrotnym. Nie można ustawić nagłówków po wysłaniu, zostanie wyświetlony komunikat o błędzie, ponieważ res.json()jest wywoływany dwukrotnie, co oznacza, że ​​wysyłane są dwie odpowiedzi.

Na jedno zapytanie można wysłać tylko jedną odpowiedź!


Błąd w powyższym przykładzie kodu był oczywisty. Bardziej typowym problemem jest posiadanie kilku oddziałów:

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

Ta trasa z dołączonym wywołaniem zwrotnym znajduje firmę w bazie danych. Wykonując zapytanie dotyczące nieistniejącej firmy, przejdziemy do else ifoddziału i wyślemy odpowiedź 404. Następnie przejdziemy do następnej instrukcji, która również wysyła odpowiedź. Teraz wysłaliśmy dwie odpowiedzi i pojawi się komunikat o błędzie. Możemy naprawić ten kod, upewniając się, że wysyłamy tylko jedną odpowiedź:

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

lub zwracając, gdy odpowiedź zostanie wysłana:

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

Wielkim grzesznikiem są funkcje asynchroniczne. Weź funkcję z tego pytania, na przykład:

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

Tutaj mamy funkcję asynchroniczną ( findOneAndUpdate()) w przykładzie kodu. Jeśli nie ma żadnych błędów ( err), findOneAndUpdate()zostanie wywołana. Ponieważ ta funkcja jest asynchroniczna, res.json(doc1)zostanie wywołana natychmiast. Załóżmy, że nie ma błędów w findOneAndUpdate(). res.json(doc2)W elsezostanie wywołana. Wysłano teraz dwie odpowiedzi i pojawia się komunikat o błędzie Nie można ustawić nagłówków .

W tym przypadku poprawką byłoby usunięcie res.json(doc1). Aby wysłać oba dokumenty z powrotem do klienta, res.json()w innym można zapisać jako res.json({ article: doc1, user: doc2 }).

Mika Sundland
źródło
2
Jesteś wewnątrz funkcji asynchronicznego i musireturnres.json
Genovo
Mój problem polegał na korzystaniu res.sendz pętli for.
Maihan Nijat,
1
Pomogło mi to na końcu zrozumieć i naprawić problem, dziękuję bardzo :)
Pankaj Parkar,
dziękuję bardzo, oszczędzasz mój czas.
Mohammad Faisal
To zdecydowanie najlepsza odpowiedź!
Juanma Menendez
53

Miałem ten sam problem i zdałem sobie sprawę, że to dlatego, że dzwoniłem res.redirectbez returninstrukcji, więc nextfunkcja została również wywołana natychmiast:

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

Które powinny być:

auth.annonymousOnly = function(req, res, next) {
    if (req.user) return res.redirect('/');
    next();
};
ergusto
źródło
43

Wiele osób dotknęło tego błędu. Jest to mylące z przetwarzaniem asynchronicznym. Najprawdopodobniej część kodu ustawia nagłówki w pierwszym ticku, a następnie w przyszłym ticku jest uruchamiane wywołanie zwrotne asynchroniczne. W międzyczasie wysyłany jest nagłówek odpowiedzi, ale następnie kolejne nagłówki (np. Przekierowanie 30X) próbują dodać dodatkowe nagłówki, ale jest już za późno, ponieważ nagłówek odpowiedzi został już przesłany.

Nie jestem pewien, co dokładnie powoduje błąd, ale spójrz na wszelkie wywołania zwrotne jako potencjalne obszary do zbadania.

Jedna prosta wskazówka, aby uprościć kod. Pozbądź się app.configure()i po prostu zadzwoń app.usebezpośrednio w zakresie najwyższego poziomu.

Zobacz także moduł everyauth , który obsługuje Facebooka i kilkanaście innych zewnętrznych dostawców uwierzytelnień.

Peter Lyons
źródło
Przekierowanie 30X to kod odpowiedzi HTTP. w3.org/Protocols/rfc2616/rfc2616-sec10.html Kody 300-399 to różne warianty przekierowania, przy czym numery 302 i 301 są powszechnie używane do wysyłania klienta pod alternatywny adres URL. Kiedy zrobisz response.redirect (...) w węźle, w odpowiedzi zostanie wysłany nagłówek przekierowania 30X.
Peter Lyons,
3
Ohhh Wyobraziłem sobie 30 przekierowań z rzędu lub coś w tym stylu
Janac Meena
17

Gotowałem głowę nad tym problemem i stało się to z powodu nieostrożnego błędu w obsłudze oddzwaniania. nieodwołane wywołania zwrotne powodują dwukrotne ustawienie odpowiedzi.!

Mój program miał kod, który sprawdza poprawność żądania i odpytuje DB. po sprawdzeniu poprawności, czy występuje błąd, oddzwoniłem do pliku index.js z błędami sprawdzania poprawności. A jeśli walidacja się powiedzie, idzie do przodu i trafia do bazy danych z sukcesem / porażką.

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

Działo się tak: sprawdzanie poprawności incase kończy się niepowodzeniem, wywołanie zwrotne zostaje wywołane, a odpowiedź ustawiona. Ale nie wrócił. Więc nadal trwa, metoda przechodzi do db i osiąga sukces / porażkę. Ponownie wywołuje to samo wywołanie zwrotne, powodując teraz ustawienie odpowiedzi dwukrotnie.

Więc rozwiązanie jest proste, musisz „zwrócić” wywołanie zwrotne, aby metoda nie kontynuowała wykonywania po wystąpieniu błędu, a zatem ustaw obiekt odpowiedzi raz

  var error = validateRequestDetails("create",queryReq);
    if (error)
        callback(error, null);
        return;
    else
       some code 
       callback(null, success);
losowość
źródło
1
Dzięki! To też był mój problem. Po prostu zrobiłem ctrl + f i znalazłem callback(...)bez return;po nim, który ostatecznie spowodował, res.send(...)że został wywołany dwukrotnie.
15

Ten rodzaj błędu pojawi się, gdy przekażesz instrukcje po wysłaniu odpowiedzi.

Na przykład:

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

res.sendSpowoduje wyświetlenie błędu, ponieważ po wysłaniu odpowiedzi następujące czynności nie zostaną wykonane.

Jeśli chcesz coś zrobić, powinieneś to zrobić przed wysłaniem odpowiedzi.

trojański
źródło
To był mój dokładny problem :)
Joel Balmer
6

Czasami może pojawić się ten błąd, gdy próbujesz wywołać funkcję next () po res.end lub res.send , spróbuj usunąć, jeśli masz next () po res.send lub res.end w swojej funkcji. Uwaga: tutaj next () oznacza, że ​​po udzieleniu odpowiedzi klientowi odpowiedzią ( tj. Res.send lub res.end ) nadal próbujesz wykonać jakiś kod, aby odpowiedzieć ponownie, więc jest to niezgodne z prawem.

Przykład:

router.get('/',function (req,res,next){
     res.send("request received");
     next(); // this will give you the above exception 
});

usuń next()z powyższej funkcji i będzie działać.

Surendra Parchuru
źródło
6

Jeśli korzystasz z funkcji oddzwaniania, użyj returnpo errbloku. Jest to jeden ze scenariuszy, w których może wystąpić ten błąd.

userModel.createUser(data, function(err, data) {
    if(err) {
      res.status = 422
      res.json(err)
      return // without this return the error can happen.
    }
    return res.json(data)
  })

Testowany na wersji Node v10.16.0i ekspresowej4.16.4

Krishnadas PC
źródło
4

Ten błąd występuje, gdy wyślesz 2 odpowiedzi. Na przykład :

if(condition A)
{ 

      res.render('Profile', {client:client_});

}

if (condition B){

      res.render('Profile', {client:client_});
    }
  }

Wyobraź sobie, że z jakiegoś powodu warunki A i B są prawdziwe, więc w drugiej chwili renderpojawi się ten błąd

Badr Bellaj
źródło
3

W moim przypadku przyczyną problemu była odpowiedź 304 (buforowanie).

Najłatwiejsze rozwiązanie:

app.disable('etag');

Alternatywne rozwiązanie tutaj, jeśli chcesz mieć większą kontrolę:

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

blured
źródło
W moim przypadku również 304 odpowiedzi. Używam włókien do przetwarzania. W każdym razie twoja odpowiedź bardzo pomaga. dziękuję
Dileep Stanley
Czy ktoś może wyjaśnić, jakie są implikacje dla usunięcia nagłówka etag?
mattwilsn
2
Znaczniki ET pozwalają serwerowi nie wysyłać treści, które nie uległy zmianie. Wyłączenie wyłącza tę funkcję. Wpis w Wikipedii ETag ( en.wikipedia.org/wiki/HTTP_ETag ) zawiera dłuższe wyjaśnienie.
pobłogosławił
3

W moim przypadku stało się tak w przypadku React i postal.js, kiedy nie wypisałem się z kanału w componentWillUnmountwywołaniu zwrotnym mojego komponentu React.

Zoltán
źródło
2

Dla każdego, kto do tego dojdzie i żadne inne rozwiązanie nie pomogło, w moim przypadku przejawiało się to na trasie, która obsługiwała przesyłanie zdjęć, ale nie radziła sobie z przekroczeniem limitu czasu , a zatem jeśli przesyłanie trwało zbyt długo i upłynęło limit czasu, gdy oddzwanianie zostało uruchomione po wysłaniu odpowiedzi o przekroczeniu limitu czasu wywołanie res.send () spowodowało awarię, ponieważ nagłówki zostały już ustawione na rozliczenie limitu czasu.

Można to łatwo odtworzyć, ustawiając bardzo krótki czas oczekiwania i uderzając w trasę z całkiem dużym obrazem, awaria była odtwarzana za każdym razem.

Mikrofon
źródło
1
jak poradziłeś sobie z przekroczeniem limitu czasu, aby tego uniknąć?
2

Po prostu to pochyliłem. Możesz przekazać odpowiedzi za pomocą tej funkcji:

app.use(function(req,res,next){
  var _send = res.send;
  var sent = false;
  res.send = function(data){
    if(sent) return;
    _send.bind(res)(data);
    sent = true;
};
  next();
});
Adam Boostani
źródło
2

Dodaj to oprogramowanie pośrednie i będzie działać

app.use(function(req,res,next){
 var _send = res.send;
var sent = false;
res.send = function(data){
    if(sent) return;
    _send.bind(res)(data);
    sent = true;
};
next();
});
ASHISH RANJAN
źródło
2

Dzieje się tak, gdy odpowiedź została dostarczona do klienta i ponownie próbujesz udzielić odpowiedzi. Musisz sprawdzić w kodzie, że gdzieś zwracasz odpowiedź klientowi, co powoduje ten błąd. Sprawdź i zwróć odpowiedź raz, kiedy chcesz wrócić.

Ankit Manchanda
źródło
1

Miałem ten problem, kiedy zagnieżdżałem obietnice. Obietnica zawarta w obietnicy zwróci 200 na serwer, ale wtedy instrukcja catch zewnętrznej obietnicy zwróci 500. Gdy to naprawię, problem zniknie.

rarding
źródło
jak dokładnie to naprawiłeś? Mam ten sam problem z obietnicami. Nie mogę ich zagnieździć ... więc jak zatrzymać wykonywanie przy instrukcji return?
saurabh
1

Przyszedł tutaj z nuxt , problem tkwił w asyncDatametodzie komponentu , zapomniałem returnobiecać, że pobierałem dane i ustawiałem tam nagłówek.

Nick Synev
źródło
1

Sprawdź, czy kod zwraca wiele instrukcji res.send () dla pojedynczego żądania. Na przykład kiedy miałem ten problem ...

Byłem tym problemem w mojej aplikacji do przywracania węzłów. To był błąd

switch (status) { case -1: res.send(400); case 0: res.send(200); default: res.send(500); }

Zajmowałem się różnymi sprawami za pomocą przełącznika bez przerwy na pisanie. Dla tych, którzy nie znają przypadku przełączników, wiedzą, że bez przerwy zwracają słowa kluczowe. Kod pod znakiem i kolejne wiersze zostaną wykonane bez względu na wszystko. Więc mimo że chcę wysłać pojedynczy res.send, z powodu tego błędu zwracał wiele instrukcji res.send, co skłoniło

błąd nie może ustawić nagłówków po ich wysłaniu do klienta. Ten problem został rozwiązany przez dodanie tego lub użycie return przed każdą metodą res.send (), np. Return res.send (200)

switch (status) { case -1: res.send(400); break; case 0: res.send(200); break; default: res.send(500); break; }

KNDheeraj
źródło
dziękuję za twoją inspirację, to samo mi się przydarzyło. Rozwiązałem to również z innym warunkiem.
Amr AbdelRahman
1

Jest bardzo prawdopodobne, że jest to raczej kwestia węzła, w 99% przypadków jest to podwójne wywołanie zwrotne, które powoduje dwukrotną odpowiedź, lub następne () dwukrotne itd., Cholera. Rozwiązało to mój problem z użyciem next () wewnątrz pętli. Usuń next () z pętli lub przestań wywoływać ją więcej niż jeden raz.

Naved Ahmad
źródło
1

Po prostu dodaję zwrotne słowo kluczowe: return res.redirect("/great");i walla!

Emmanuel Benson
źródło
1

Miałem ten sam problem, który spowodował mangusta.

naprawić, które musisz włączyć Promises, abyś mógł dodać: mongoose.Promise = global.Promisedo swojego kodu, który umożliwia używanie native js promises.

inne alternatywy dla tego rozwiązania to:

var mongoose = require('mongoose');
// set Promise provider to bluebird
mongoose.Promise = require('bluebird');

i

// q
mongoose.Promise = require('q').Promise;

ale najpierw musisz zainstalować te pakiety.

sina
źródło
1

błąd znajdź sam po RND:

1) mój kod błędu:

return res.sendStatus(200).json({ data: result });

2) mój kod sukcesu

return res.status(200).json({ data: result });

różnica polega na tym, że użyłem sendStatus () zamiast status () .

nagender pratap chauhan
źródło
0

W maszynopisie moim problemem było to, że nie zamknąłem połączenia internetowego po otrzymaniu wiadomości.

WebSocket.on("message", (data) => {
    receivedMessage = true;
    doSomething(data);
    localSocket.close(); //This close the connection, allowing 
});
Janac Meena
źródło
0

Jeśli nie otrzymujesz pomocy z góry: dla noobs Przyczyną tego błędu jest wielokrotne wysyłanie żądania, daj nam zrozumieć z niektórych przypadków: - 1. `

module.exports = (req,res,next)=>{
        try{
           const decoded  = jwt.verify(req.body.token,"secret");
           req.userData = decoded;
           next();
        }catch(error){
            return res.status(401).json({message:'Auth failed'});
        }
        next();   
        }

`w powyższym wywołaniu next () dwa razy spowoduje błąd

  1. router.delete('/:orderId', (req, res, next) => { Order.remove({_id:req.params.orderId},(err,data)=>{ if(err){ **res.status(500).json(err);** }else{ res.status(200).json(data); } *res.status(200).json(data);* }) })

tutaj odpowiedź jest wysyłana dwukrotnie, sprawdź, czy już wysłałeś odpowiedź

Sanjay
źródło
0

W moim przypadku dzieje się tak z powodu wielu wywołań zwrotnych. next()Wielokrotnie wywoływałem metodę podczas kodu

Pan Ratnadeep
źródło
0

Mój problem polegał na tym, że miałem setIntervalbieg, który miał if/elseblok, w którym clearIntervalmetoda znajdowała się w else:

      const dataExistsInterval = setInterval(async () => {
        const dataExists = Object.keys(req.body).length !== 0;
        if (dataExists) {
          if (!req.files.length) {
            return res.json({ msg: false });
          } else {
              clearInterval(dataExistsInterval);
            try {
            . . .

Stawianie clearIntervalprzed tym if/elsezrobiło lewę.

Mike K.
źródło
0

W moim przypadku, w pętli, umieściłem, res.render()więc mogłem próbować dzwonić wiele razy.

Hasan Sefa Ozalp
źródło
-1

Wszystko, co musiałem zrobić w przypadku tego błędu, to res.end ().

 auth.annonymousOnly = function(req, res, next) {
 // add other task here   
   res.end();    
  };

Innym problemem, z którym możesz się spotkać, jest kod po res.json i res. pisać. W takim przypadku musisz użyć return, aby zatrzymać wykonywanie.

 auth.annonymousOnly = function(req, res, next) {

  if(!req.body.name)
  {
    res.json({error:"some error"});
    return;
  }
  let comp = "value"; // this is the code after res.json which may cause some problems so you have to use return 
};
koder
źródło