2017-10-20 5 views
0

Je travaille sur une fonctionnalité de 'changement de mot de passe'. Je commence à en apprendre davantage au sujet des promesses et ont le code suivant:Comment éviter les 'en-têtes déjà envoyés' dans la chaîne Promise?

router.post('/change-password', verifyToken, csrfProtection, (req, res, next) => { 
    if (!req.body.password_current || !req.body.password_new) { 
    req.flash('info', 'Please fill in both fields.'); 
    return res.redirect('/change-password'); 
    } 
    const data = {}; 
    data.password = req.body.password_new; 
    tokenHandler.verifyToken(req.cookies.token) 
    .then((decoded) => { 
     return User.findOne({ '_id.user_id': decoded.user }); 
    }) 
    .then((user) => { 
     data.userId = ObjectId(user._id.user_id); 
     return bcrypt.compare(req.body.password_current, user.password); 
    }) 
    .then((allowed) => { 
     if (!allowed) { 
     return res.redirect('/change-password'); 
     } 
     console.log('I am not here'); 
     return User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }, { new: true }); 
    }) 
    .then(() => { 
     return res.redirect('/change-password'); 
    }) 
    .catch((err) => { 
     return next(err); 
    }); 
}); 

J'aime la façon dont les promesses empêchent l'enfer de rappel '. Le problème est que je reçois une erreur «en-têtes déjà envoyés». Je sais que c'est parce que je ne peux pas échapper à la chaîne et qu'elle enregistre tous les résultats (sauf si vous lancez une erreur). Pour résoudre le problème je les suivantes:

router.post('/change-password', verifyToken, csrfProtection, (req, res, next) => { 
    if (!req.body.password_current || !req.body.password_new) { 
    req.flash('info', 'Please fill in both fields.'); 
    return res.redirect('/change-password'); 
    } 
    const data = {}; 
    data.password = req.body.password_new; 
    tokenHandler.verifyToken(req.cookies.token) 
    .then((decoded) => { 
     User.findOne({ '_id.user_id': decoded.user }).then((user) => { 
     data.userId = ObjectId(user._id.user_id); 
     bcrypt.compare(req.body.password_current, user.password).then((allowed) => { 
      if (!allowed) { 
      return res.redirect('/change-password'); 
      } 
      User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }).then((doc) => { 
      console.log(doc); 
      return res.redirect('/change-password'); 
      }); 
     }); 
     }); 
    }); 
}); 

La question est: Y at-il une meilleure solution pour corriger l'erreur 'en-tête déjà envoyé. Parce que j'ai le sentiment que ma solution est en réalité à quelques pas d'une structure «callback hell».

+0

Vous avez seulement besoin d'imbriquer la partie de la chaîne 'then' qui est supposée aller dans la partie' else' de la condition, pas tout. De même, vous ne devriez pas supprimer '.catch()' à la fin. – Bergi

+0

Voulez-vous vraiment faire la même chose 'res.redirect ('/ change-password');' même si elles ne sont pas 'allowed'? Dans ce cas, n'appelez pas 'res.redirect' deux fois, mais placez le' return User.findOneAndUpdate (...) 'dans un if (autorisé)' et continuez normalement la chaîne vers l'appel de redirection. – Bergi

+0

Merci pour votre réponse et rappel que j'ai oublié le bloc '.catch()'. :-) J'ai besoin des redirections pour envoyer un message 'flash' différent. J'aurais pu trouver d'autres solutions pour éviter l'erreur 'header already sent'. Mais j'étais vraiment curieux de savoir comment je pouvais l'empêcher avec cette structure. Maintenant je sais! – Jeffrey

Répondre

1

Vous pouvez réécrire comme ça

router.post('/change-password', verifyToken, csrfProtection, (req, res, next) => { 
    if (!req.body.password_current || !req.body.password_new) { 
    req.flash('info', 'Please fill in both fields.'); 
    return res.redirect('/change-password'); 
    } 
    const data = {}; 
    data.password = req.body.password_new; 
    tokenHandler.verifyToken(req.cookies.token) 
    .then((decoded) => { 
     return User.findOne({ '_id.user_id': decoded.user }); 
    }) 
    .then((user) => { 
     data.userId = ObjectId(user._id.user_id); 
     return bcrypt.compare(req.body.password_current, user.password); 
    }) 
    .then((allowed) => { 
     if (!allowed) { 
     return res.redirect('/change-password'); 
     } 
    else{ 
     console.log('I am not here'); 
     return User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }, { new: true }) 
      .then(() => { 
       return res.redirect('/change-password'); 
      }); 
     }  
    }) 
    .catch((err) => { 
     return next(err); 
    }); 
}); 

Vous pouvez retourner une chaîne de promesse dans une fonction then.

+0

Merci! Cela a tellement plus de sens maintenant. :-) – Jeffrey

0

Selon votre version de Node, vous pouvez également réécrire ceci en utilisant async/await. Cela rend généralement les choses plus faciles à raisonner.

router.post('/change-password', verifyToken, csrfProtection, async (req, res, next) => { 
if (!req.body.password_current || !req.body.password_new) { 
    req.flash('info', 'Please fill in both fields.'); 
    return res.redirect('/change-password'); 
} 

try { 
    const data = {}; 
    data.password = req.body.password_new; 
    const decoded = await tokenHandler.verifyToken(req.cookies.token); 
    const user = await User.findOne({ '_id.user_id': decoded.user }); 
    data.userId = ObjectId(user._id.user_id); 
    const allowed = await bcrypt.compare(req.body.password_current, user.password); 
    if (!allowed) { 
     return res.redirect('/change-password'); 
    } else { 
     await User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }, { new: true }); 
    } 
    return res.redirect('/change-password'); 
} catch (err) { 
    return next(err); 
} 
}); 

Vous avez besoin de Node.js> = 7 pour utiliser async/await.

+0

Merci! J'ai essayé l'async/wait et ça marche! J'ai seulement eu à emballer le code entier dans un IIFE. '(async() => {})();' – Jeffrey