2016-07-28 4 views
0

Ich versuche, eine NodeJS-Anwendung mit Express zu entwickeln. Hier ist mein CodeNodeJS, Express, MySQL - Header kann nicht gesetzt werden, nachdem sie gesendet wurden

app.post('/open', checkStatus, function(req, res) { 
    if (req.error) { 
    console.log(req.log); 
    return res.json(req.error); 
    } 

    console.log(currentDate() + colors.gray('>> ') + colors.bold(colors.cyan(req.method)) + ' ' + colors.green('200') + ' ' + req.url); 

    var data = req.body; 

    status.door.isOpening = true; 

    setUser(data, function() { 
    setOpening(data, function() { 
     openTheDoorPlease(data, function(response, log) { 
     if (status.door.isOpening) { 
      status.door.isOpening = false; 
      console.log(log); 
      return res.json(response); 
     } 
     }); 
    }); 
    }); 
}); 

und hier der Fehler

throw err; // Rethrow non-MySQL errors 
     ^

Error: Can't set headers after they are sent. 
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11) 
    at ServerResponse.header (/Users/yourmajesty/Sites/arduino-test/server/node_modules/express/lib/response.js:719:10) 
    at ServerResponse.send (/Users/yourmajesty/Sites/arduino-test/server/node_modules/express/lib/response.js:164:12) 
    at ServerResponse.json (/Users/yourmajesty/Sites/arduino-test/server/node_modules/express/lib/response.js:250:15) 
    at Query.<anonymous> (/Users/yourmajesty/Sites/arduino-test/server/server.js:179:22) 
    at Query.<anonymous> (/Users/yourmajesty/Sites/arduino-test/server/server.js:255:50) 
    at Query.<anonymous> (/Users/yourmajesty/Sites/arduino-test/server/server.js:276:42) 
    at Query._callback (/Users/yourmajesty/Sites/arduino-test/server/server.js:321:46) 
    at Query.Sequence.end (/Users/yourmajesty/Sites/arduino-test/server/node_modules/mysql/lib/protocol/sequences/Sequence.js:85:24) 
    at Query._handleFinalResultPacket (/Users/yourmajesty/Sites/arduino-test/server/node_modules/mysql/lib/protocol/sequences/Query.js:144:8) 

Eigentlich, wenn die URL '/ open' mit POST-Methode, das erste Mal aufgerufen wird, ist alles ok. Aber das zweite Mal habe ich diesen Fehler. Alle Antworten, die ich gefunden habe, sprechen über die return.

Der gesamte Code ist hier verfügbar: https://gist.github.com/fcordillot/48428cfccc260635672b9e27d86b5d07

Wer kann mir helfen?

+0

'status.door.isOpening = true;' sieht so aus, als würden Sie globale Daten auf Ihrem Server einstellen. Ist das wirklich, was Sie vorhaben - Daten ändern, die alle Anfragen von allen Benutzern sehen und verwenden? – jfriend00

+0

Wo ist diese Zeile in Ihrem Code 'throw err; // Wiederhole keine MySQL-Fehler? Sie sagen uns, dass das der Ursprung ist, aber Sie zeigen uns nicht den damit verbundenen Code. Das müssen wir sehen. – jfriend00

+0

Außerdem verfügt Ihr Anforderungshandler über Codepfade, in denen keine Antwort gesendet wird. Das ist auch nicht korrekt. – jfriend00

Antwort

1

Hier sind einige Probleme mit Ihrem Code:

  1. Sie erscheinen mit status.door.isOpening = true; globalen Zustand zu ändern, die alle Benutzer auswirkt. Es sieht auch so aus, als ob Sie Rassenbedingungen mit diesem globalen Status haben könnten.

  2. Es gibt Pfade durch den Anforderungshandler, die keine Antwort zurückgeben. Zum Beispiel, wenn falsch ist, dann senden Sie niemals eine Antwort.

  3. In openTheDoorPlease() fügen Sie bei jedem Aufruf einen Ereignishandler hinzu. Somit wird jedes Mal, wenn Sie es aufrufen, ein anderer Event-Handler installiert und Sie erhalten eine doppelte Antwortbehandlung und rufen Ihren Callback mehrmals auf und versuchen so, die Antwort mehrmals zu senden. Dies ist wahrscheinlich das Problem, das die Meldung "Header kann nicht gesetzt werden, nachdem sie gesendet wurden" verursacht.

In dieser Funktion gibt es Probleme:

/*---------- Actions on Arduino ----------*/ 
function openTheDoorPlease(data, callback) { 
    // 
    // Do stuff to open the door here 
    // 
    // 
    socket.emit('event', { 
    type: 'open-door' 
    }); 

    socket.on('event', function(data) { 
    switch (data.type) { 
     case 'door-opened': 
     if (status.door.isOpening) { 
      doorOpened(function(response, log) { 
      if (callback !== undefined) callback.call(this, response, log); 
      }); 
     } 
     break; 
     default: 
     break; 
    } 
    }); 

} 

Wenn Sie eine Steckdose Event-Handler werden hinzuzufügen, dann MÜSSEN Sie entfernen, wenn das Ereignis sie nicht anhäufen auftritt, so tun. Aber dieses Design wird im Allgemeinen nicht zuverlässig funktionieren, da es den Rennbedingungen unterliegt. Socket.io ist einfach kein Anfrage/Antwort-Protokoll. Wenn Sie ein Ereignis ausgeben und dann auf ein Antwort-Ereignis warten, können Sie nicht wissen, welche Antwort zu welcher Anfrage gehört. Wenn mehrere Benutzer Ihr System verwenden, können sie leicht miteinander verwechseln, welche Ereignisse wohin gehen. Dieser Code muss wahrscheinlich auf eine andere Weise erneuert werden.

  1. Eine weitere globale Variable Ausgabe

In diesem Code:

/*---------- Socket ----------*/ 
var socket; 
io.on('connection', function(sock) { 
    socket = sock; 

    status.socket.isReady = true; 
}); 

Sie versuchen, eine Verbindung von Clientverbindung in einer globalen Variablen zu bleiben. Das bedeutet, dass Ihr Server nur zuverlässig mit einem Benutzer arbeiten wird. Es funktioniert einfach nicht ordnungsgemäß mit mehreren Benutzern, die mit Ihrem Server verbunden sind.Sie können diesen Weg in keiner Serverumgebung codieren. Server verarbeiten Anfragen von vielen verschiedenen Benutzern und alle haben dieselbe globale Umgebung. Was Sie wahrscheinlich tun müssen, ist in der Lage zu sagen (von einer bestimmten HTTP-Anfrage), welche socket.io Verbindung zu diesem Benutzer gehört, damit Sie diese Verbindung erhalten und Daten an sie senden können.

  1. In ähnlicher Weise scheint Ihre checkStatus() Funktion den globalen Status überall zu verwenden.

Sofern Sie eine Client/Server-Umgebung zu entwerfen beabsichtigen, die nur jemals entworfen wurde zu einer Zeit mit einem Client zu arbeiten, viele dieser Konstruktion überdacht werden muss, und nochmals gemacht zu vermeiden, mit globaler Shared Zustand. Wenn Sie den Status auf dem Server beibehalten müssen, möchten Sie wahrscheinlich, dass der größte Teil dieses Status benutzerdefiniert ist (wahrscheinlich mithilfe eines Sitzungsobjekts oder ähnlichem). Jeder globale Status muss explizit für die gemeinsame Nutzung durch alle Benutzer vorgesehen sein. Der Zugriff auf den globalen Status muss sicher sein, wenn mehrere Benutzer gleichzeitig auf den Server zugreifen.

0

Zwei Dinge, zuerst müssen nicht Rückkehr verwenden. Überprüfen Sie, ob Ihre Funktionen (setUser, setOpening, openTheDoorPlease usw.) keine Header oder Antworten senden. Hier ist Ihr Code nicht mit Rückkehr.

app.post('/open', checkStatus, function(req, res) { 
    if (req.error) { 
    console.log(req.log); 
    res.json(req.error); 
    } 

    console.log(currentDate() + colors.gray('>> ') + colors.bold(colors.cyan(req.method)) + ' ' + colors.green('200') + ' ' + req.url); 

    var data = req.body; 

    status.door.isOpening = true; 

    setUser(data, function() { 
    setOpening(data, function() { 
     openTheDoorPlease(data, function(response, log) { 
     if (status.door.isOpening) { 
      status.door.isOpening = false; 
      console.log(log); 
      res.json(response); 
     } 
     }); 
    }); 
    }); 
}); 
+0

Danke für Ihre Antwort! Sie können den gesamten Code hier finden: https://gist.github.com/fcordillot/48428cfccc260635672b9e27d86b5d07 –

+0

Ich habe keine Umgebung hier zu testen, aber Kommentar von Zeile 50 bis 57 (Ihre Middleware, die Header bearbeiten) und sagen Sie mir ob es funktioniert. – Martin

+0

Nein, das gleiche Problem mit den Zeilen kommentiert ... –

Verwandte Themen