Search code examples
javascriptnode.jsexpresspostnunjucks

Node.js error: 'Can't set headers after they are sent' on post


Ok, this topic as been answered a lot of times on stackoverflow, but I didn't find anything usefull for me.

I've setup a nodejs server and the 'get' queries works fine but when I'm trying to post, I get this error and it's written 'Cannot post /fr/login' (the url is domain.com/fr/login)

Just so you know, I'm using Express, i18n and Nunjucks as a templating engine.

I really don't know what's going on. I'm trying to make a login system but this is stopping me to do so. Sa bassically what's going on, is that the user input it's infos and then I call User.findOne with the mail and the password and then it should trigger the callback with the error that happen (successful login is considered an error). Currently, it does callback and 'transfer' to the callback the user information. (You might see a console.log in the callback, it is working). But when it triggers the res.render I get the two errors.

Here are my files:

  1. server.js

    app.post('/:lang/login', getChangelog, getAlert, function(req, res) { 
    
      i18n.setLocale(req, req.params.lang);
        let version;
        if(req.cookies.version) {
           version = req.cookies.version;
        }
        if(!version) version = 0;
    
        var email = req.body.mailconnect;
        var password = req.body.mdpconnect;
    
        User.findOne({ email: email, password: password }, function(err, user){
          if(user['error'] != "LOGIN OK") {
            console.log(user);
            errorInfo['showError'] = true;
            errorInfo['error-msg'] = req.__("Error-WrongMail");
            res.render('login/login.njk', { title: req.__('Login-PageTitle'), 
    style: 'login.css', alertInfo: alertInfo, errorInfo: errorInfo, version: version, currVersion: currVersion, firstVersion: firstVersion, changelog: changelog });
          } else {
            var hash = user["mdp"];
            hash = hash.replace(/^\$2y(.+)$/i, '$2a$1');
            bcrypt.compare(password, hash, function(err, res2) {
              if(res2){
                errorInfo['showError'] = false;
                errorInfo['error-msg'] = "LOGIN OK";
    
                res.redirect(i18n.getLocale + '/agent/' + user["id"] + '/selectservice');
              } else{
                errorInfo['showError'] = true;
                errorInfo['error-msg'] = req.__("Error-MailAndPassNoMatch");
                res.render('login/login.njk', { title: req.__('Login-PageTitle'), style: 'login.css', alertInfo: alertInfo, errorInfo: errorInfo, version: version, currVersion: currVersion, firstVersion: firstVersion, changelog: changelog });
              }
            });
          }
        });
      });
    
  2. user.js

    var db = require('../other/mysql.js');
    exports.findOne = (data, callback) => {
    if(data.email != "" && data.password != "") {
      db.connection.query('SELECT * FROM membres WHERE mail = ?', data.email)
       .on('result', function (row) {
    
         let user = new Object();
         user = row;
         user['error'] = 'WrongMail'; //Temporary, just for testing
    
         callback(null, user);
    
       })
       .on('error', function (err) {
         let user = new Object();
         user['error'] = 'SQL';
         callback(err, user});
      });
     }
    }
    
  3. mysql.js

    var mysql = require('mysql');
    
    var pool = mysql.createPool({
      connectionLimit : 10,
      host: "ip",
      user: "user",
      password: "password",
      database: "databse"
    });
    
    exports.connection = {
        query: function () {
            var queryArgs = Array.prototype.slice.call(arguments),
                events = [],
                eventNameIndex = {};
    
    pool.getConnection(function (err, conn) {
        if (err) {
            if (eventNameIndex.error) {
                eventNameIndex.error();
            }
        }
        if (conn) { 
            var q = conn.query.apply(conn, queryArgs);
            q.on('end', function () {
                conn.release();
            });
    
            events.forEach(function (args) {
                q.on.apply(q, args);
            });
        }
    });
    
    return {
        on: function (eventName, callback) {
            events.push(Array.prototype.slice.call(arguments));
            eventNameIndex[eventName] = callback;
            return this;
        }
    };
    }
    };
    
  4. This is my nodejs log

    RowDataPacket {
       id: 1,
       mail: 'mymail',
    
    
        matricule: '972',
        mdp: 'an hashed password',
        num_vehicle: 8001,
        profession: 'agent',
        dispatch: 'Y',
        supervisor: 'Y',
        admin: 'Y',
        status: 1,
        tkn: '3faixj17guocz89zfdx5a9',
        serv: '',
        error: 'WrongMail' }
    
    
        Caught exception:  Error: Can't set headers after they are sent.
           at validateHeader (_http_outgoing.js:491:11)
           at ServerResponse.setHeader (_http_outgoing.js:498:3)
           at ServerResponse.header (F:\GitHub\terminal.ga\node_modules\express\lib\response.js:730:10)
           at ServerResponse.send (F:\GitHub\terminal.ga\node_modules\express\lib\response.js:170:12)
    at done (F:\GitHub\terminal.ga\node_modules\express\lib\response.js:967:10)
    at F:\GitHub\terminal.ga\node_modules\nunjucks\src\environment.js:37:5
    at RawTask.call (F:\GitHub\terminal.ga\node_modules\asap\asap.js:40:19)
    at flush (F:\GitHub\terminal.ga\node_modules\asap\raw.js:50:29)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)
    

Solution

  • I found out what was my problem. The query returned results more than on time so it was calling the callback more than one time.

    To fix this, I created a new exports that just get a connection from the pool and perform a query just like in the docs. A very simpel way to be sure it does not send data more than one time.