使用async/await仍出现cannot set headers after sent错误是什么原因?
问题原因
你遇到的报错和是否使用async/await无关,核心是逻辑流程没有终止:validateUser内调用res.status().json()已经向客户端返回了响应,但函数内的return仅会结束validateUser自身的执行,外层的register接口函数拿到返回值后,依然会继续执行密码哈希、创建用户、再次调用res.status(200).json()的逻辑,两次调用响应方法就会触发cannot set headers after sent错误。
修复方案
提供两种常用的合规修复方式:
方案1:校验函数返回校验结果,外层判断后终止流程
修改validateUser,不要在校验函数内直接返回响应,而是返回校验状态和提示:
module.exports = async (user) => { if(!user.name) return { pass: false, msg: 'name is required' } if(user.name.length < 5) { return { pass: false, msg: 'name must have more than 5 characters' } } return { pass: true } }
修改注册接口调用逻辑,校验不通过时直接return终止整个接口执行:
exports.register = catchAsync(async (req, res, next) => { let toCreate = {} mapUsers(toCreate, req.body) const validateRes = await validateUser(toCreate) if(!validateRes.pass) { return res.status(400).json({ msg: validateRes.msg }) } toCreate.password = await bcrypt.hashPassword(toCreate.password) let newUser = await User.create({...toCreate}) res.status(200).json({ msg: 'user created', newUser }) })
方案2:校验失败抛出错误,由全局错误中间件统一处理
更符合Express错误处理规范的写法,校验失败时直接抛出带状态码的错误,由你外层封装的catchAsync捕获后传递给全局错误中间件返回响应:
修改validateUser:
module.exports = async (user) => { if(!user.name) { const err = new Error('name is required') err.statusCode = 400 throw err } if(user.name.length < 5) { const err = new Error('name must have more than 5 characters') err.statusCode = 400 throw err } }
这种写法下注册接口无需额外判断,只要await validateUser执行没有抛出异常,就说明校验通过,直接走后续逻辑即可。
内容的提问来源于stack exchange,提问作者Arpit
相关产品推荐
相关产品推荐

