为何Express登录接口try catch块中会出现未处理的Promise拒绝?
问题根源
你遇到的ERR_HTTP_HEADERS_SENT报错和未处理Promise拒绝的核心原因是:验证失败调用res.json()后没有终止代码执行,导致后续逻辑继续运行,尝试第二次发送HTTP响应。
问题出在两段短路判断代码上:
!user && res.status(401).json("Invalid credentials!");
originalPassword !== req.body.password && res.status(401).json("Invalid credentials!");
调用res.json()仅会把响应返回给客户端,并不会终止当前接口函数的执行,后续代码还是会继续向下运行:
- 如果用户名不存在,
user为null,返回401响应后仍会执行CryptoJS.AES.decrypt(user.password,...),访问null的password属性直接抛出异常,触发catch块尝试再次发送500响应,就会触发响应头已发送的报错。 - 如果密码错误,返回401响应后仍会执行后续的
res.status(200).json(others),同样会尝试二次发送响应触发报错。
解决方法
只需要在每个验证失败的分支调用res.json()后,增加return终止函数后续逻辑执行即可,修正后的代码如下:
router.post("/login", async (req, res) => { try { const user = await User.findOne({ username: req.body.username }); if (!user) { return res.status(401).json("Invalid credentials!"); } const hashedPassword = CryptoJS.AES.decrypt( user.password, process.env.PASS_SEC ); const originalPassword = hashedPassword.toString(CryptoJS.enc.Utf8); if (originalPassword !== req.body.password) { return res.status(401).json("Invalid credentials!"); } const { password, ...others } = user._doc; res.status(200).json(others); } catch (error) { res.status(500).json(error.message); } });
修改后每个分支只会发送一次响应,不会再触发二次写响应头的逻辑,对应的Promise未处理异常也会同步消失。
内容的提问来源于stack exchange,提问作者Anthony Dinino
相关产品推荐
相关产品推荐

