简体   繁体   中英

How to fix “Error: Can't set headers after they are sent” in Express

I have recently been developing a MERN application and I have recently came into the trouble that express is saying that I am setting headers after they are sent.

I am using mongo db and trying to update a user profile.

I have tried to comment out my res.send points to find the issue but I have failed to do so.

Here is my post method for updating the user profile:

app.post("/api/account/update", (req, res) => {
    const { body } = req;
    // Validating and Checking Email
    if (body.email) {
      var email = body.email;
      email = email.toLowerCase();
      email = email.trim();
      body.email = email;
      User.find(
        {
          email: body.email
        },
        (err, previousUsers) => {
          if (previousUsers.length > 0) {
            return res.send({
              success: false,
              message:
                "Error: There is already another account with that email address"
            });
          } else {
          }
        }
      );
    }
    // Validating Names Function
    function checkName(name) {
      var alphaExp = /^[a-zA-Z]+$/;
      if (!name.match(alphaExp)) {
        return res.send({
          success: false,
          message: "Error: Names cannot contain special characters or numbers"
        });
      }
    }
    checkName(body.firstName);
    checkName(body.lastName);

    // Making sure that all fields cannot be empty
    if (!body.email && !body.firstName && !body.lastName) {
      return res.send({
        success: false,
        message: "Error: You cannot submit nothing"
      });
    }
    // Getting User ID from the current session
    UserSession.findById(body.tokenID, function(err, userData) {
      // Finding User ID using the current users session token
      if (userData.isDeleted) {
        return res.send({
          success: false,
          message:
            "Error: Session token is no longer valid, please login to recieve a new one"
        });
      }
      // Deleting the token ID from the body object as user table entry doesnt store tokens
      delete body.tokenID;
      // Finding the user profile and updating fields that are present
      User.findByIdAndUpdate(userData.userId, body, function(err, userInfo) {
        if (!err) {
          return res.send({
            success: true,
            message: "Success: User was updated successfully"
          });
        }
      });
    });
  });

This is the call that I am doing to the backend of the site:

onUpdateProfile: function(fieldsObj) {
    return new Promise(function(resolve, reject) {
      // Get Session Token
      const obj = getFromStorage("the_main_app");
      // Defining what fields are getting updated
      fieldsObj.tokenID = obj.token;
      // Post request to backend
      fetch("/api/account/update", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(fieldsObj)
      })
        .then(res => {
          console.log("Verify Token - Res");
          return res.json();
        })
        .then(json => {
          console.log("Verify Token JSON", json);
          if (json.success) {
            window.location.href = `/manage-account?success=${json.success}`;
          } else {
            window.location.href = `/manage-account?success=${json.success}`;
          }
        });
    });
  }

Here is my error message that I am getting:

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 (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:767:10)
    at ServerResponse.send (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:170:12)
    at ServerResponse.json (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:267:15)
    at ServerResponse.send (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:158:21)
    at C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\routes\api\account.js:270:22
    at C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\mongoose\lib\model.js:4641:16
    at process.nextTick (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\mongoose\lib\query.js:2624:28)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)
[nodemon] app crashed - waiting for file changes before starting...

Can anyone help me with this?

EDIT

I have changed my code, this seems to now work however I feel like its a little messy when put together. Any refactoring tips?

Code:

app.post("/api/account/update", (req, res) => {
    // Preform checks on data that is passed through
    const { body } = req;
    var messages = {
      ExistedUser:
        "Error: There is already another account with that email address",
      NameFormat: "Error: Names cannot contain special characters or numbers",
      BlankInputs: "Error: You cannot submit nothing",
      accountLoggedOut:
        "Error: Session token is no longer valid, please login to recieve a new one",
      successfullyUpdated: "Success: User was updated successfully"
    };
    var usersFound;
    if (body.email) {
      var email = body.email;
      email = email.toLowerCase();
      email = email.trim();
      body.email = email;
      User.find(
        {
          email: body.email
        },
        (err, UserCount) => {
          usersFound = UserCount;
        }
      );
    }
    function capitalize(text) {
      return text.replace(/\b\w/g, function(m) {
        return m.toUpperCase();
      });
    }
    if (body.firstName) {
      body.firstName = capitalize(body.firstName);
    }
    if (body.lastName) {
      body.lastName = capitalize(body.lastName);
    }

    //Making sure that all fields cannot be empty
    if (!body.email && !body.firstName && !body.lastName) {
      return res.send({
        success: false,
        message: messages.BlankInputs
      });
    }
    // Getting User ID from the current session
    UserSession.findById(body.tokenID, function(err, userData) {
      // Finding User ID using the current users session token
      if (userData.isDeleted) {
        return res.end({
          success: false,
          message: messages.accountLoggedOut
        });
      }
      if (userData) {
        // Deleting the token ID from the body object as user table entry doesnt store tokens
        delete body.tokenID;
        // Finding the user profile and updating fields that are present
        User.findByIdAndUpdate(userData.userId, body, function(err, userInfo) {
          if (userInfo) {
            if (!usersFound.length > 0) {
              return res.send({
                success: true,
                message: messages.successfullyUpdated
              });
            } else {
              return res.send({
                success: false,
                message: messages.ExistedUser
              });
            }
          }
        });
      }
    });
  });

You're calling res.send() twice. res.send() ends the process. You ought to refactor such that you call res.write() and only call res.send() when you're done.

This StackOverflow link describes the difference in more detail. What is the difference between res.send and res.write in express?

I believe this is happening, as you're trying to send a response after the first / initial response has already been sent to the browser. For example:

checkName(body.firstName);
checkName(body.lastName);

Running this function twice is going to try and yield 2 different "response" messages.

The product of a single route, should ultimately be a single response.

Thanks for all your help on this issue.

Here is my final code that allowed it to work.

I have also tried to "refactor" it too. Let me know if you'd do something else.

app.post("/api/account/update", (req, res) => {
    const { body } = req;
    console.log(body, "Logged body");
    // Defining objects to be used at the end of request
    var updateUserInfo = {
      userInfo: {},
      sessionToken: body.tokenID
    };
    var hasErrors = {
      errors: {}
    };

    // Checking that there is at least one value to update
    if (!body.email && !body.firstName && !body.lastName) {
      var blankError = {
        success: false,
        message: "Error: You cannot change your details to nothing"
      };
      hasErrors.errors = { ...hasErrors.errors, ...blankError };
    } else {
      console.log("Normal Body", body);
      clean(body);
      console.log("Cleaned Body", body);
      updateUserInfo.userInfo = body;
      delete updateUserInfo.userInfo.tokenID;
    }
    // Function to check if object is empty
    function isEmpty(obj) {
      if (Object.keys(obj).length === 0) {
        return true;
      } else {
        return false;
      }
    }

    // Function to remove objects from body if blank
    function clean(obj) {
      for (var propName in obj) {
        if (obj[propName] === "" || obj[propName] === null) {
          delete obj[propName];
        }
      }
    }

    // Checking and Formatting Names Given
    function capitalize(text) {
      return text.replace(/\b\w/g, function(m) {
        return m.toUpperCase();
      });
    }
    if (body.firstName) {
      body.firstName = capitalize(body.firstName);
    }
    if (body.lastName) {
      body.lastName = capitalize(body.lastName);
    }

    // Checking and formatting email
    if (body.email) {
      body.email = body.email.toLowerCase();
      body.email = body.email.trim();

      // Checking for email in database
      User.find({ email: body.email }, (err, EmailsFound) => {
        if (EmailsFound.length > 0) {
          var EmailsFoundErr = {
            success: false,
            message: "There is already an account with that email address"
          };
          hasErrors.errors = { ...hasErrors.errors, ...EmailsFoundErr };
        }
      });
    }
    // Getting User Session Token
    UserSession.findById(updateUserInfo.sessionToken, function(err, userData) {
      // Finding User ID using the current users session token
      if (userData.isDeleted) {
        var userDeletedError = {
          success: false,
          message:
            "Your account is currently logged out, you must login to change account details"
        };
        hasErrors.errors = { ...hasErrors.errors, ...userDeletedError };
      } else {
        // Finding the user profile and updating fields that are present
        User.findByIdAndUpdate(
          userData.userId,
          updateUserInfo.userInfo,
          function(err, userInfo) {
            // userInfo varable contains user db entry
            if (err) {
              var updateUserError = {
                success: false,
                message: "Error: Server Error"
              };
              hasErrors.errors = {
                ...hasErrors.errors,
                ...updateUserError
              };
            }
            if (isEmpty(hasErrors.errors)) {
              res.send({
                success: true,
                message: "Success: You have updated your profile!"
              });
            } else {
              res.send({
                success: false,
                message: hasErrors.errors
              });
            }
          }
        );
      }
    });
  });

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