2017-10-20 19 views
0

我正在研究'更改密碼'功能。我開始瞭解更多關於承諾,並有以下代碼:如何避免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); 
    }); 
}); 

我很喜歡承諾是如何防止'回調地獄'。問題是我收到'headers already sent'錯誤。我知道那是因爲我無法逃脫這個鏈條,並且它保存了所有的結果(除非你拋出一個錯誤)。爲了解決這個問題,我使用了以下內容:

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'); 
      }); 
     }); 
     }); 
    }); 
}); 

問題是:是否有更好的解決方案來修復'header already sent'錯誤。因爲我覺得我的解決方案實際上離「回撥地獄」結構只有幾步之遙。

+0

您只需要嵌套'then'鏈中應該進入條件的'else'部分的部分,而不是所有部分。你也不應該在最後刪除'.catch()'。 – Bergi

+0

你真的想要'res.redirect'('/ change-password');'即使它們不被'允許'嗎?在這種情況下,不要調用'res.redirect'兩次,而是將'User.findOneAndUpdate(...)'返回到'if(允許)'內並繼續鏈接到重定向調用。 – Bergi

+0

謝謝你的回答,並提醒我忘了'.catch()'塊。 :-)我需要重定向來傳遞不同的'flash'消息。我本可以找到其他解決方案來防止'header already sent'錯誤。但我真的很好奇,如何用這種結構來阻止它。現在我明白了! – Jeffrey

回答

1

你可以把它改寫這樣

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); 
    }); 
}); 

您可以從一個then函數內返回一個承諾鏈。

+0

謝謝!現在它變得更有意義。 :-) – Jeffrey

0

根據您的Node版本,您也可以使用async/await重新編寫此版本。它通常會讓事情更容易推理。

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); 
} 
}); 

您需要Node.js> = 7才能使用async/await。

+0

謝謝!我已經嘗試了異步/等待,它的工作原理!我只需要將整個代碼包裝在一個IIFE中。 '(async()=> {})();' – Jeffrey