在中间件日志之间正确传递数据但 returns 在对象内部未定义

Passing data between middleware logs correctly but returns undefined inside object

我在两个快速中间件之间传递数据,因为我的一个变量设置在函数(中间件 1)中并且需要在其函数范围之外(在中间件 2 中)进行访问。当我 console.log req.invoice 在我的第二个中间件中它正确记录所以我知道我已经正确地在中间件之间传递了数据但是当我试图使用我的变量在我的第二个中间件中构造一个新对象时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.invoice这样的路径是我的第一个中间件。

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

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

您有异步计时问题。在您的第一个中间件中,您在 Invoice.find() 函数完成之前调用 next(),因此在设置 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. generateInvoice() 函数中移动了 fileName, dest, invoiceNr 的变量声明,因此它们对于函数的每次调用都是唯一的,并且同时进行中的其他请求不会破坏它们的值。

其他潜在问题:

  1. 您声明 generate() 接受一个错误参数,但您没有将参数传递给它。
  2. generate() 中的 throw err 如果被击中,将不会被捕获并做任何有用的事情。
  3. 您在构建 PDF 文档时没有任何错误处理。
  4. 如果 PDF 文件的任何构造是异步的,则在让下一个中间件尝试使用它之前您不会等待它完成,因此可能存在尚未完成写入的竞争条件在您尝试使用它之前。