You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:36:17