正确地在中间件日志之间传递数据但在对象内返回未定义的内容

时间:2016-07-09 23:59:17

标签: javascript node.js express mailgun

我在两个快速中间件之间传递数据,因为我的一个变量是在一个函数(中间件1)中设置的,需要在其功能范围之外访问(在中间件2中)。当我在我的第二个中间件中使用console.log req.invoice时,它正确记录,所以我知道我已经正确地传递了中间件之间的数据,但是当我尝试使用我的变量在我的第二个中间件中构造一个新对象时,请求。发票未定义。

var express = require('express');
var app = express();
var Invoice = require('../models/Invoice');
var router = express.Router();
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var expressValidator = require('express-validator');
var fs = require('fs');
//Used to create a pdf invoice
var PDFDocument = require('pdfkit');

//Route
router.route('/:item')
  .post(generateInvoice, sendMail, function(req, res){

  });

//First middleware

var fileName, dest, invoiceNr;
function generateInvoice (req, res, next) {
  //Destination for storing the invoice file
  dest = __dirname + '/../static/';
  //generate invoice nr
   Invoice.find(function(err, invoices){
    if(err) {
     return res.send(err);
    } else {
      invoiceNr = invoices.length + 1;
      fileName = 'invoice' + invoiceNr + '.pdf';
      req.invoicePath = path.resolve(dest + fileName);
      generate();
    }
  });
  //Create the invoice and store in static directory 
  function write() {
    doc = new PDFDocument();
    doc.pipe(fs.createWriteStream(dest + fileName));
    doc.text(invoice, 100, 100);
    console.log('File written > ' + fileName + '\n Destination: ' + dest);
    doc.end();
  }
  function generate (err){
    if (err)
      throw err;
    if (invoiceNr !== undefined) {
      write();
    }
  }

  next();

}

//Second middleware
//I'm using mailgun-js to send the invoice via email
function sendMail(req, res, next){
  //Mailgun implementation
  var api_key = 'MY_KEY';
  var domain = 'MY_DOMAIN';
  var mailgun = require('mailgun-js')({apiKey: api_key, domain: domain});
  var data = {
    from: 'APP_MAIL',
    to: 'example@mail.com',
    subject: 'Hello',
    text: 'Should include attachment!',
    //req.invoicePath is undefined when it should be a filepath
    attachment: req.invoicePath
    //when invoicePath was set as a static string, the attachment was included in the email
    //attachment: '/Users/anton/Desktop/app/src/server/static/invoice27.pdf'
  };

  //again I'm using mailgun-js for sending the emails
  mailgun.messages().send(data, function (error, body) {
    console.log('Message body: ' + body);
    //This works and I get the above: '/Users/anton/Desktop...' in the console
    console.log('The path to the invoice: ' + req.invoicePath);
    //Works properly as well
    console.log('The path is of type: ' + typeof(req.invoicePath));
  });
  res.end();
}

我设置了req.invoicePath,就像这是我的第一个中间件。

req.invoicePath = path.resolve(dest + fileName);

有关如何使用mailgun发送电子邮件的简要说明,请参阅mailgun blog here 任何帮助都非常感谢,谢谢!

1 个答案:

答案 0 :(得分:0)

您有异步时序问题。在您的第一个中间件中,您在next()函数完成之前调用Invoice.find(),从而在设置req.invoicePath之前执行第二个中间件。

要修复,只有在完成第一个中间件中的异步操作时才调用next()。您还需要在generateInvoice()内移动变量,以便它们是受保护的局部变量,并且不会被另一个同时处于飞行中的请求所破坏:

function generateInvoice (req, res, next) {
  var fileName, dest, invoiceNr;
  //Destination for storing the invoice file
  dest = __dirname + '/../static/';
  //generate invoice nr
   Invoice.find(function(err, invoices){
    if(err) {
     return res.send(err);
    } else {
      invoiceNr = invoices.length + 1;
      fileName = 'invoice' + invoiceNr + '.pdf';
      req.invoicePath = path.resolve(dest + fileName);
      generate();
      // move next() here so it is not called until after req.invoicePath is set
      next();
    }
  });
  //Create the invoice and store in static directory 
  function write() {
    var doc = new PDFDocument();
    doc.pipe(fs.createWriteStream(dest + fileName));
    doc.text(invoice, 100, 100);
    console.log('File written > ' + fileName + '\n Destination: ' + dest);
    doc.end();
  }
  function generate (err){
    if (err)
      throw err;
    if (invoiceNr !== undefined) {
      write();
    }
  }
}

此处还可能存在其他异步问题,因为我假设write()可能有一些异步部分。并且,您向generate()显示一个参数,但是您没有传递参数。并且,如果generate()执行throw err,则您没有任何处理程序可以执行智能操作。

我所做的改变:

  1. next()移至Invoice.find()回调内,以便在设置req.invoicePath之后才会调用它。
  2. fileName, dest, invoiceNr函数内移动了generateInvoice()的变量声明,因此它们对于函数的每次调用都是唯一的,同时在飞行中的其他请求不会破坏它们的值。
  3. 其他潜在问题:

    1. 您声明generate()接受错误的参数,但您没有将其传递给它。
    2. throw err中的generate()不会被抓住,如果被击中就会做任何有用的事情。
    3. 您在构建PDF文档时没有任何错误处理。
    4. 如果PDF文件的任何构造是异步的,那么在让下一个中间件试图使用它之前你不是在等待它完成所以可能存在一个竞争条件,它还没有在你之前写完我正试着用它。