简体   繁体   中英

HTTP POST request error “Error: Can't set headers after they are sent.”

I am running into a problem with a particular part of my code. I have managed to isolate it to this one chunk. It is a POST request which sends back a paymentSuccess : true object if everything is successful. The front end will then redirect the user to the /confirmation URL.

However, as soon as the POST request is completed, the following error appears: Error: Can't set headers after they are sent.

Currently stuck on what could be causing the problem and have looked at other questions. I don't see anything interfering and hope that someone could provide some insight.

Below is my code, it is a bit lengthly, I know:

// POST Registration Database Queries
router.post('/checkout', function(req, res, next) {

   // Constants for use in queries
   var dateRegistered = new Date();
   var userIDs = [];
   var billingID;

   // Register athletes if they don't already exist
   pool.getConnection(function(err, connection){
      connection.beginTransaction(function(err) {
         if (err) {
            throw err;
         }

         // Check to if users are already registered and register them in the database if not
         _.each(req.body.users, function(val, key){
            var existsQuery = 'SELECT `userId`, `firstName`, `lastName`, `email`, `dateRegistered` from Database.Users WHERE firstName = ? AND lastName = ? AND email = ?';
            // Check to see if the user is already in the database
            connection.query(existsQuery, [val.firstName, val.lastName, val.email], function(err, result){
               if (err) {
                  return connection.rollback(function() {
                     console.log(err);
                     res.send({ registerSuccess: false });
                  });
               } else if(_.isEmpty(result)){ // If the user doesn't exist
                  var registerQuery = 'INSERT INTO Database.Users (`firstName`, `lastName`, `email`, `dateRegistered`) VALUE ( ?, ?, ?, ?)';
                  connection.query(registerQuery, [val.firstName, val.lastName, val.email, dateRegistered], function(err, result){
                     if (err) {
                        return connection.rollback(function() {
                           console.log(err);
                           res.send({ registerSuccess: false });
                        });
                     } else {
                        console.log(result);
                        connection.commit(function(err) {
                           if (err) {
                              return connection.rollback(function() {
                                 console.log(err);
                                 res.send({ registerSuccess: false });
                              });
                           } else {
                              console.log("User registered successfully!");
                              userIDs.push(result.insertId);
                              res.send({ registerSuccess: true });
                           }
                        });
                     }
                  });
               } else { // If the user already exists
                  console.log("User already exists!");
                  userIDs.push(result[0].userId);
                  res.send({ registerSuccess: true });
               }
            });
         });

      });
   });

   // Register billing user into the database if they don't already exist
   pool.getConnection(function(err, connection){
      connection.beginTransaction(function(err) {
         if (err) {
            throw err;
         }

         // Confirm if payment is successful before continuing
         gateway.transaction.sale({
            amount: req.body.payment.totalCost,
            paymentMethodNonce: req.body.paymentData.paymentNonce,
            customer: {
               firstName: req.body.billing.firstName,
               lastName: req.body.billing.lastName,
               // company: "Braintree",
               phone: req.body.billing.phoneNumber,
               // fax: "312-555-12346",
               // website: "http://www.example.com",
               email: req.body.billing.email
            },
            billing: {
               firstName: req.body.billing.firstName,
               lastName: req.body.billing.lastName,
               // company: "Braintree",
               streetAddress: req.body.billing.address,
               // extendedAddress: "Suite 403",
               locality: req.body.billing.city,
               region: req.body.billing.province,
               postalCode: req.body.billing.postalCode,
               countryCodeAlpha2: "CA"
            },
            options: {
               submitForSettlement: true
            },
         }, function(err, result) {
            var paymentResult = result;
            if (result.success == true) {
               // If the payment is successful, make the queries for the entries
               var existsQuery = 'SELECT `userId`, `firstName`, `lastName`, `email`, `dateRegistered` from Database.Users WHERE firstName = ? AND lastName = ? AND email = ?';
               // Check to see if the user is already in the database
               connection.query(existsQuery, [req.body.billing.firstName, req.body.billing.lastName, req.body.billing.email], function(err, result){
                  if (err) {
                     return connection.rollback(function() {
                        console.log(err);
                        res.send({ paymentSuccess : false });
                     });
                  } else if(_.isEmpty(result)){ // If the user doesn't already exist
                     console.log("Billing user does not exist!");
                     sql = 'INSERT INTO Database.Users (`firstName`, `lastName`, `email`, `address`, `cellNumber`, `city`, `phoneNumber`, `postalCode`, `province`, `dateRegistered`) VALUE ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
                     data = [req.body.billing.firstName, req.body.billing.lastName, req.body.billing.email, req.body.billing.address, req.body.billing.cellNumber, req.body.billing.city, req.body.billing.phoneNumber, req.body.billing.postalCode, req.body.billing.province, dateRegistered];
                  } else { // If the user already exists
                     console.log("Billing user already exists!");
                     billingID = result[0].userId;
                     sql = 'UPDATE Database.Users SET `address` = ?, `cellNumber` = ?, `city` = ?, `phoneNumber` = ?, `postalCode` = ?, `province` = ? WHERE userID = ?';
                     data = [req.body.billing.address, req.body.billing.cellNumber, req.body.billing.city, req.body.billing.phoneNumber, req.body.billing.postalCode, req.body.billing.province, billingID];
                  }
                  // Add or update the billing user
                  connection.query(sql, data, function(err, result){
                     if (err) {
                        return connection.rollback(function() {
                           console.log(err);
                           res.send({ paymentSuccess : false });
                        });
                     } else {
                        // Insert transaction into the database
                        var fees = (paymentResult.transaction.amount * .029) + 0.3;
                        var timeStamp = new Date();
                        var rawResponse = JSON.stringify(paymentResult.transaction);
                        var transactionID = paymentResult.transaction.id;
                        var totalCost = paymentResult.transaction.amount;
                        var transaction = [billingID, paymentResult.transaction.id, paymentResult.transaction.merchantAccountId, paymentResult.transaction.amount, fees, paymentResult.transaction.currencyIsoCode, paymentResult.transaction.paymentInstrumentType, timeStamp, 0, 0, rawResponse];
                        connection.query('INSERT INTO Database.Transactions (userID, braintreeTransactionID, braintreeMerchantAccount, amount, fees, currency, paymentInstrumentType, timeStamp, processed, refunded, rawResponse) VALUE (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', transaction, function(err, result) {
                           if (err) {
                              return connection.rollback(function() {
                                 console.log(err);
                                 res.send({ paymentSuccess : false });
                              });
                           } else {
                              console.log("Transaction has been recorded!");
                              // Record tournament entries
                              for(j = 0; j < userIDs.length; j++){
                                 var tournamentUser = [userIDs[j], req.body.tournament.chosen, result.insertId, "Temp Val"];
                                 var sql = "INSERT INTO Database.tournamentEntries (userID, tournamentID, transactionID, skillLevel) VALUE (?, ?, ?, ?)";
                                 connection.query(sql, tournamentUser, function(err, result){
                                    if (err) {
                                       return connection.rollback(function() {
                                          console.log(err);
                                          res.send({ paymentSuccess : false });
                                       });
                                    } else {
                                       connection.commit(function(err) {
                                          if (err) {
                                             return connection.rollback(function() {
                                                console.log(err);
                                                res.send({ paymentSuccess: false });
                                             });
                                          } else {
                                             console.log("Tournament entries were successful!");
                                             userIDs.push(result.insertId);
                                             res.send({ paymentSuccess : true });
                                          }
                                       });
                                    }
                                 });
                              }
                           }
                        });
                     }
                  });
               });
            } else { // Payment has failed
               res.send({ paymentSuccess : false });
            }
         });

      });
   });

});

_.each is a synchronous/blocking call. However, inside that you are make db.query calls which are asynchronous. So basically you will not know which of the callbacks are sending error or data and whether your code should stop at that.

Due to asynchronous nature of your methods, even if one the res.send method is called due to error or success, other async calls are not terminated since they are already fired up by _.each method.

You would need to fire those either one by one or parallel but most importantly need a final callback which should get called when any one them has error or all of them completed successfully and then only you should make the res.send method.

I would suggest having look at the async npm module. Look at async.each or async.whilst method. This can probably be written using async as follows -

async.each(req.body.users, function(item, callback){
    // call your db methods here ... 
    // once they are done for error call callback(error) when  successfull call with callback(null)
}, function(err){
    // the final call back 
   // if any of the query produced an error, err would equal that error
   if(error){
     res.send("error");
   }
   else 
   {
      // success
      res.send("success");
   }
});

I see that you have two registrations going on inside the same route and you are calling res.send from inside both of them.

So in that case, the one that finishes first is the response you get on the client side and the one that finished next is causing the error.

Instead of calling res.send from everywhere you can call it inside a callback and inside the callback you can check if both the registrations are completed and only then send the response.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM