Node.js新手求助:如何将Nodemailer封装为Express中间件
如何正确封装Nodemailer为Express中间件/可复用函数
问题原因
你用app.use(Mailer)报错的核心原因是:Mailer是transporter.sendMail()返回的Promise对象,而Express中间件要求必须是一个接受(req, res, next)三个参数的函数,两者类型不匹配,自然会抛出错误。
另外,你原来的external.js代码一导入就会立即执行sendMail,这也不符合Express的使用逻辑——我们通常希望在收到请求时才触发邮件发送,而不是服务器启动就发。
正确封装方式
方式1:封装为可复用的邮件发送函数(推荐)
这种方式灵活性更高,可以在任意路由或逻辑中调用:
external.js
import nodemailer from 'nodemailer'; // 初始化transporter(只创建一次,避免重复初始化) const transporter = nodemailer.createTransport({ service: 'gmail', auth: { user: 'me@gmail.com', pass: 'jdwpkdszkmhmuwvnb' } }); // 导出发送邮件的异步函数 export const sendEmail = async (mailOptions) => { try { const info = await transporter.sendMail(mailOptions); console.log('Email sent: ' + info.response); return info; } catch (error) { console.error('邮件发送失败:', error); throw error; // 抛出错误让调用者处理 } }; // 可导出默认邮件选项,方便复用 export const defaultMailOptions = { from: 'me@gmail.com', to: 'friend@gmail.com', subject: 'Sending Email using Node.js', text: 'That was easy Man!' };
app.js
import express from 'express'; import cors from 'cors'; import { sendEmail, defaultMailOptions } from './external.js'; const app = express(); const PORT = process.env.PORT || 5000; // CORS配置(保持原有逻辑) let corsOrigins = []; if (process.env.REQUESR_ORIGIN) { corsOrigins = [process.env.REQUESR_ORIGIN]; } else { corsOrigins = ["http://localhost:3000", "*"]; } const corsOptions = { origin: corsOrigins, methods: ['GET', 'POST'], allowedHeaders: ['Content-Type', 'Authorization'] }; // 中间件 app.use(cors(corsOptions)); app.use(express.json()); // 示例1:固定内容发送邮件的路由 app.get('/send-email', async (req, res) => { try { await sendEmail(defaultMailOptions); res.status(200).send('邮件发送成功'); } catch (error) { res.status(500).send(`邮件发送失败:${error.message}`); } }); // 示例2:接受前端参数的自定义邮件路由 app.post('/send-custom-email', async (req, res) => { try { const { to, subject, text } = req.body; const customOptions = { ...defaultMailOptions, to, subject, text }; await sendEmail(customOptions); res.status(200).send('自定义邮件发送成功'); } catch (error) { res.status(500).send(`邮件发送失败:${error.message}`); } }); app.listen(PORT, () => { console.log(`服务器运行在端口 ${PORT}`); });
方式2:封装为Express中间件(适合特定路由前置逻辑)
如果你需要在某个路由执行前自动发送邮件,可以封装成符合Express规范的中间件:
external.js(新增中间件导出)
// 接上面的代码,新增: export const emailMiddleware = async (req, res, next) => { try { await transporter.sendMail(defaultMailOptions); console.log('邮件已发送,继续执行后续逻辑'); next(); // 调用next()让请求继续向下传递 } catch (error) { console.error('邮件发送失败:', error); res.status(500).send('邮件发送失败,请求终止'); // 这里不调用next(),请求链会终止 } };
app.js中使用中间件:
import { emailMiddleware } from './external.js'; // 路由使用中间件:先发送邮件,再返回响应 app.get('/test-middleware', emailMiddleware, (req, res) => { res.send('邮件发送成功,后续业务逻辑执行完毕'); });
注意事项
- 不要在模块导入时直接执行
sendMail,否则服务器启动就会发送邮件,不符合业务逻辑。 - 使用
async/await处理异步邮件发送逻辑,避免回调嵌套,同时方便错误捕获。 - Gmail的授权码(
pass字段)需要在Google账户中开启“应用专用密码”,不要用账户原密码。
内容的提问来源于stack exchange,提问作者user17880509
相关产品推荐
相关产品推荐

