【问题标题】:How to include this nodemailer function inside this controller function?如何在这个控制器函数中包含这个 nodemailer 函数?
【发布时间】:2020-02-01 14:31:27
【问题描述】:

我正在尝试创建一个表单,该表单首先将数据发送到 Mongo DB,然后将通过 Nodemailer 的电子邮件发送该数据。以下是 2 个功能:

控制器功能

exports.createListing = (req, res) => {
    // Validate request
    if(!req.body.content) {
        return res.status(400).send({
            message: "Fields can not be empty"
        });
    }

    const listing = new Listing({
        title: req.body.title, 
        city: req.body.city,
        street: req.body.street,
        businessname: req.body.businessname,
        description: req.body.description
    });

    listing.save()
    .then(data => {
        res.send(data);
    }).catch(err => {
        res.status(500).send({
            message: err.message || "Some error occurred while creating the listing."
        });
    });
};

NodeMailer 功能

 var smtpTransport = nodemailer.createTransport({
        service: 'Gmail',
        port: 465,
        auth: {
          user: 'YOUR_GMAIL_SERVER',
          pass: 'YOUR_GMAIL_PASSWORD'
        }
      });

      var mailOptions = {
        to: data.email,
        subject: 'ENTER_YOUR_SUBJECT',
        html: `<p>${data.title}</p>
              <p>${data.city}</p>
              <p>${data.street}</p>`,
              ...
      };

      smtpTransport.sendMail(mailOptions,
        (error, response) => {
          if (error) {
            res.send(error)
          } else {
            res.send('Success')
          }
          smtpTransport.close();
        });

如何在上述创建列表功能中包含此 Nodemailer 部分,以及如何在电子邮件正文中包含提交的数据。我认为电子邮件正文中的当前 data.title 和其他选项是错误的方式。

【问题讨论】:

    标签: node.js mongodb express mongoose nodemailer


    【解决方案1】:

    创建单独的 mail.js 或 anyname.js

    var config  = require('../config/config.js');
    var nodemailer = require('nodemailer');
    
    var smtpTransport = nodemailer.createTransport({
        service :"gmail",
        host: "smtp.gmail.com",
        auth :
        {
            user: config.email,
            pass: config.password
        }
    });
    
    
    // setup email data with unicode symbols
    var mailOptions = {
        from: config.email,
        to: 'user to send',
        subject :'message',
        text :' "Hi",\n You have successfully created an account"',
        html: '<b>Welcome?</b>' // html body
    };
    
    // sends mail
    module.exports.sendMail  = function()
    {
     // send mail with defined transport object
     smtpTransport.sendMail(mailOptions, (error, info) => {
        if (error)
        {
            return console.log(error);
        }
        console.log('Message sent: %s', info.messageId);});
    }
    

    现在将此文件导入控制器 js 文件中

    var mailer = require('./mail.js');
    

    并像下面这样使用它

    mailer.sendMail()
    

    您可以在 sendMail 函数中传递值或参数,并在 mail.js 文件中访问它们以创建自定义消息或标题或任意名称

    【讨论】:

    • 嗨,关键是我需要放置提交的数据,例如标题:req.body.title,城市:req.body.city,街道:req.body.street,企业名称:req.body.businessname,描述:req.body.description 在电子邮件正文中。
    【解决方案2】:

    我建议围绕 nodemailer 创建一个包装模块,因此您可以多次重用 sendEmail 函数。

    为自己创建一个名为email-client.js 或任何你想要的文件。在这个模块中,您可以在smtpTransport 上创建一个闭包,并且只导出sendEmail 函数。

    电子邮件客户端

    const nodemailer = require("nodemailer");
    
    const smtpTransport = nodemailer.createTransport({
        service: "Gmail",
        port: 465,
        auth: {
            user: "YOUR_GMAIL_SERVER",
            pass: "YOUR_GMAIL_PASSWORD"
        }
    });
    
    async function sendMail({ to, subject, html }) {
        return smtpTransport.sendMail({ to, subject, html });
    }
    
    module.exports = {
        sendMail
    };
    

    注意:smtpTransport.sendMail 返回一个 Promise,我们将在您的控制器内部处理它。

    控制器

    首先,您可以导入从email-client.js 导出的sendEmail 函数,然后您可以在控制器中使用它。请注意,我将控制器更改为 async 并更喜欢猫鼬 Model.create(使测试更容易一些)。

    const { sendEmail } = require("./email-client.js");
    
    exports.createListing = async (req, res) => {
        try {
            if (!req.body.content) {
                return res.status(400).send({
                    message: "Fields can not be empty"
                });
            }
    
            const listing = await Listing.create({
                title: req.body.title,
                city: req.body.city,
                street: req.body.street,
                businessname: req.body.businessname,
                description: req.body.description
            });
    
            await sendEmail({
                to: "blabla",
                subject: "blabla",
                html: `<p>${listing.title}</p>
            <p>${listing.city}</p>
            <p>${listing.street}</p>`
            });
    
            return res.send("Success");
        } catch (error) {
            return res.status(500).send({
                message:
                    error.message ||
                    "Some error occurred while creating the listing."
            });
        }
    };
    

    【讨论】:

    • sendMail 不返回承诺。至少它不是来自 nodemailer 的原生形式。所以你不能只添加return smtpTransport.sendMail(。您似乎也误解了 async 关键字的作用。它不会使某些东西“异步”或承诺。它只允许在声明它的内部块上使用await
    • await sendMail({ 我猜是有错误吧?
    • @NeilLunn @NeilLunn nodemailers documentaion 在'example'中你可以清楚地看到await transporter.sendMail({...}) 你也可以这样node mailer usage docs,在底部,“如果没有设置回调参数,那么该方法返回一个承诺对象。”最后,我不明白您对 async/await 用法的评论,使用函数的 async 关键字 infont 将“告诉他们返回一个承诺而不是直接返回值”
    • @NewTechLover 您需要将sendEmail 功能导入您的控制器,我已添加到 sn-p 中。
    • 很公平。考虑到所有其他实际使用示例“一目了然”,这可能应该在文档中更加突出,只显示 callback 用法。
    【解决方案3】:

    这里最简单的形式是将函数与回调(nodemailer one)包装在 Promise 中:

    exports.createListing = (req, res) => {
        // Validate request
        if(!req.body.content) {
            return res.status(400).send({
                message: "Fields can not be empty"
            });
        }
    
        // Set options after the request was verified.
    
        const smtpTransport = nodemailer.createTransport({
            service: 'Gmail',
            port: 465,
            auth: {
              user: 'YOUR_GMAIL_SERVER',
              pass: 'YOUR_GMAIL_PASSWORD'
            }
        });
    
        const listing = new Listing({
            title: req.body.title, 
            city: req.body.city,
            street: req.body.street,
            businessname: req.body.businessname,
            description: req.body.description
        });
    
        listing.save()
        .then(data => new Promise((resolve, reject) => {
          var mailOptions = {
            to: data.email,
            subject: 'ENTER_YOUR_SUBJECT',
            html: `<p>${data.title}</p>
                  <p>${data.city}</p>
                  <p>${data.street}</p>`,
                  ...
          };
    
          smtpTransport.sendMail(mailOptions,
            (error, response) => {
              if (error) {
                reject(error);
              } else {
                resolve(data);
              }
    
            });
    
        })
        .then(data => {
          smtpTransport.close(); // this awaited the actual send
          res.send(data); 
        }
        .catch(err => {
            res.status(500).send({
                message: err.message || "Some error occurred while creating the listing."
            });
        });
    };
    

    请注意,这里的resolve(data) 有效地将结果传递到 Promise 链中的下一个链接,这比在同一范围内嵌套 Promise 链只是为了访问相同的值要好。然后,当任一方法失败时,您还可以使用 catch() 的单点。

    也就是说,当前的 API 在没有回调的情况下调用时实际上会返回 Promise,但您可能需要 asyncawait 语法以使访问更清晰:

    exports.createListing = async (req, res) => { // <-- mark block as async
        // Validate request
        if(!req.body.content) {
            return res.status(400).send({
                message: "Fields can not be empty"
            });
        }
    
        // Set options after the request was verified.
    
        const smtpTransport = nodemailer.createTransport({
            service: 'Gmail',
            port: 465,
            auth: {
              user: 'YOUR_GMAIL_SERVER',
              pass: 'YOUR_GMAIL_PASSWORD'
            }
        });
    
        const listing = new Listing({
            title: req.body.title, 
            city: req.body.city,
            street: req.body.street,
            businessname: req.body.businessname,
            description: req.body.description
        });
    
        try {                                    // try..catch for error handling
    
          let data = await listing.save();       // await the save
    
          var mailOptions = {
            to: data.email,
            subject: 'ENTER_YOUR_SUBJECT',
            html: `<p>${data.title}</p>
                  <p>${data.city}</p>
                  <p>${data.street}</p>`,
                  ...
          };
    
          await smtpTransport.sendMail(mailOptions);   // await the sendMail
    
          smtpTransport.close(); // this awaited the actual send
          res.send(data); 
        } catch(err) {
          res.status(500).send({
             essage: err.message || "Some error occurred while creating the listing."
        }
    };
    

    同样重要的是要注意这种方法是串行执行的。因此,除非数据正确保存,否则此处不会发送邮件。这可能是也可能不是您想要的情况,但简单地创建包装 Promise 至少应该是正确的方向。

    【讨论】:

    • ${data.title}

      ${data.city}

      ${data.street}

      `,是从上面保存的列表详细信息中获取此数据?
    • @NewTechLover 是的。这就是为什么它在save() 之后直接内联。这可以通过data 解决,然后您设置的邮件内容可以访问data 及其所有属性。也是为什么我向您解释resolve(data) 的原因相同,下一个then() 可以访问相同的data 值。
    • Neil 在将来自第二个模型的数据放入同一函数后,我遇到了类似的问题:如果应该从 2 个模型中添加数据怎么办? stackoverflow.com/questions/58267043/…
    【解决方案4】:

    导出您的 sendMail 方法并将其导入您的控制器。

    控制器功能

    let sendMail = require('your nodemailer file').sendMail;
    exports.createListing = (req, res) => {
        // Validate request
        if(!req.body.content) {
            return res.status(400).send({
                message: "Fields can not be empty"
            });
        }
    
        const listing = new Listing({
            title: req.body.title, 
            city: req.body.city,
            street: req.body.street,
            businessname: req.body.businessname,
            description: req.body.description
        });
    
        listing.save()
        .then(data => {
            sendMail({
            title: req.body.title, 
            city: req.body.city,
            street: req.body.street})
            res.send(data);
        }).catch(err => {
            res.status(500).send({
                message: err.message || "Some error occurred while creating the listing."
            });
        });
    };
    

    NodeMailer函数

    var smtpTransport = nodemailer.createTransport({
            service: 'Gmail',
            port: 465,
            auth: {
              user: 'YOUR_GMAIL_SERVER',
              pass: 'YOUR_GMAIL_PASSWORD'
            }
          });
    
    
    
    module.exports.sendmail = (data)=>{
    return new Promise((resolve,reject)=>{
    
          var mailOptions = {
            to: data.email,
            subject: 'ENTER_YOUR_SUBJECT',
            html: `<p>${data.title}</p>
                  <p>${data.city}</p>
                  <p>${data.street}</p>`,
                  ...
          };
    
    
    
    
    
          smtpTransport.sendMail(mailOptions,
            (error, response) => {
              if (error) {
                reject(error);
              } else {
                resolve('Success');
              }
              smtpTransport.close();
            });
    });
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-30
      • 2013-09-28
      • 2021-01-28
      • 1970-01-01
      • 2017-05-22
      相关资源
      最近更新 更多