使用Nodemailer发送邮件成功却出现ERR_HTTP_HEADERS_SENT错误求助
解决Nodemailer发送邮件时出现的ERR_HTTP_HEADERS_SENT错误
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
问题原因
你遇到的这个错误核心是多次向客户端发送HTTP响应,具体问题点:
- 路由逻辑中重复发送响应:保存订阅邮箱后,你先调用
res.status(200).json(...)返回了一次结果,之后又执行return res.status(200).send(link)再次发送响应,触发重复响应错误。 sendEmail函数错误处理响应:函数内部尝试调用res.status(200).json(...),但该函数并未传入res对象(会导致未定义错误),即使传入也会再次触发响应发送,加剧问题。sendEmail是async函数,但使用了回调版的transporter.sendMail,导致await sendEmail(...)无法真正等待邮件发送完成,时序逻辑混乱。
修复步骤
1. 重构sendEmail为Promise风格
将回调改为Promise实现,让async/await能正确工作,并且不在函数内部处理响应,统一交给路由层处理:
const sendEmail = async (email, subject, payload, template) => { try { const transporter = nodemailer.createTransport({ service: "gmail", auth: { user: process.env.EMAIL_USERNAME, pass: process.env.EMAIL_PASSWORD, }, }); const options = { from: process.env.FROM_EMAIL, to: email, subject: subject, html: template, }; // 使用Promise版sendMail,移除回调逻辑 const info = await transporter.sendMail(options); return { success: true, info }; } catch (error) { throw error; } };
2. 修复路由中的重复响应问题
调整路由逻辑,确保所有操作(保存邮箱+发送邮件)完成后只发送一次响应:
router.post("/", async (req, res, next) => { try { if (!req.body.newsletterEmail) { return next(createError(400, "Please enter your email")); } else if (!isEmail(req.body.newsletterEmail)) { // 移除重复的!req.body.newsletterEmail判断,已在前序逻辑校验过 return next(createError(400, "Please enter a valid email address")); } const checkEmail = await SubscribedEmails.findOne({ newsletterEmail: req.body.newsletterEmail }); if (checkEmail) { return next(createError(400, "Email is already subscribed")); } // 创建并保存订阅邮箱 const newSubscribedEmail = new SubscribedEmails({ newsletterEmail: req.body.newsletterEmail, }); const SubscribedEmail = await newSubscribedEmail.save(); const link = `${process.env.CLIENT_URL}/`; // 等待邮件发送完成 await sendEmail( SubscribedEmail.newsletterEmail, "Welcome to our newsletter!", { link: link }, `<div> <p>Hi,</p> <p>You are subscribed to our.</p> <p> Please click the link below to view more</p> <a href=${link}>GO</a> </div>` ); // 仅发送一次响应,整合需要返回的信息 return res.status(200).json({ message: "Successfully subscribed", SubscribedEmail, link }); } catch(err) { next(err); } });
额外优化点
- 移除路由中冗余的条件判断,简化逻辑
- 确保
sendEmail函数不再依赖外部的res对象,避免未定义错误
内容的提问来源于stack exchange,提问作者sultan.h
相关产品推荐
相关产品推荐

