NodeJS报Cannot set headers after they are sent错误如何解决
错误根因
Error [ERR_HTTP_HEADERS_SENT] 报错的本质是同一个HTTP请求流程中,服务端多次向客户端发送响应(即多次调用res.json()/res.send()/res.end()等方法),Express 不允许重复设置响应头/返回响应。
代码问题点
问题出在sendResetPasswordMail的错误处理逻辑:
- 当发邮件出现异常时,
sendResetPasswordMail内部的catch块会调用catchError(err, next),正常情况下这个工具方法会调用Express错误处理中间件,直接向客户端返回错误响应。 - 但
sendResetPasswordMail的catch块捕获错误后没有向外抛出异常,所以上层Controller的await sendResetPasswordMail(...)不会触发自身的catch块,会继续向下执行res.status(200).json({ message: SuccessMessages.resetPassword }),此时就出现了两次返回响应的情况,触发报错。
修复方案
可二选一:
方案1:移除sendResetPasswordMail内部的错误捕获,把异常完全交给上层Controller处理
修改后sendResetPasswordMail代码如下:
const sendResetPasswordMail: SendResetPasswordMail = async (userEmail: string, token: string): Promise<void> => { const transporter = nodemailer.createTransport({ service: 'gmail', auth: { user: adminEmail.email, pass: adminEmail.pass } }) const mailOptions = { from: adminEmail.email, to: userEmail, subject: 'Reset password', text: `Reset password: http://localhost:3000/resetPassword?token=${token}` } await transporter.sendMail(mailOptions) }
调用时也不需要传入next参数即可,发邮件异常会直接进入Controller的catch块走统一错误处理,不会继续执行后续200返回逻辑。
方案2:在sendResetPasswordMail的catch块抛出异常,通知上层执行出错
修改后sendResetPasswordMail的catch块代码如下:
catch (err: any) { catchError(err, next) throw err // 抛出错误,中断上层后续逻辑执行 }
额外校验建议
可以确认下catchError工具方法的实现,确保它确实会调用res相关方法返回响应,这是触发本次报错的前提。
内容的提问来源于stack exchange,提问作者George Hutanu
相关产品推荐
相关产品推荐

