2015-04-02 49 views
0

多个中间件我有一个ExpressJS应用这需要形式的数据并执行以下操作: 1.检查所需的所有值被提供, 2.验证数据是有效的, 3.将记录添加到数据库以获得唯一的ID, 4.使用ID和数据调用单独的服务器, 5.根据来自服务器的响应,更新数据库记录并提供响应的详细信息。ExpressJS连接到回调

我使用mongoskin作为数据库。

我的问题与我如何控制流量有关。基本上我已经将上述每个步骤都写成了中间件函数,因为我需要在每个回调函数上调用next()成功(或者下一个错误)。

看来我正在编写太多的中间件,应该能够将这些步骤分组到更大的包含多个'子函数'的中间件集合中,但我不知道如何在Express中执行此操作,因为我需要每次异步函数调用完成时调用next()。有没有正确的方法来做到这一点,或者这是'一个中间件每步'的方法真的是正确的方式来运行?

编辑:张贴一些代码作为请求。这是为了简洁起见,部分代码:

function validateFields(req, res, next) { 
    //... 
    //iterate over req.body to confirm all fields provided 
    //... 
    if (allDataProvided) { 
     //... 
     //iterate over req.body to confirm all fields valid 
     //... 
     if (allDataValid) { 
      return(next()); 
     } else { 
      return(next(err)); 
     } 
    } else { 
     return(next(err)); 
    } 
}, 

//get an auto incrementing ID fields from a mongodb collection (counters) 
function getNextID(req, res, next) { 
    counters.findAndModify(
     { _id: "receiptid" }, 
     [['_id','asc']], 
     { $inc: { seq: 1 } }, 
     {}, 
     function(err, doc) { 
      if (err) { 
       return next(err); 
      } else { 
       req.receiptid = doc.seq; 
       return next(); 
      } 
     }); 
}, 

//insert a new record into the transaction collection (txns) using the new ID 
function createTransaction(req, res, next) { 
    txns.insert(
     { _id : req.receiptid, 
      body : req.body, 
      status : "pending"}, 
      {}, 
      function(err, r) { 
      if (err) { 
       return next(err); 
      } else { 
       return next(); 
      } 
     }); 
}, 

//process the data on the remote web service using the provider's API (remoteapi) 
function processTransaction(req, res, next) { 
    remoteapi.processTransaction(
     { data: req.body, 
      receiptid: req.receiptid }, 
      function(err, r) { 
      if (err) { 
       return next(err); 
      } else { 
       req.txnReceipt = r; 
       return next(); 
      } 
     }); 
}, 

//update the record in the database collection (txns) with the server response 
function updateDatabase(req, res, next) { 
    txns.updateById(req.receiptid, 
        { $set :{status : "success", 
          receipt: req.txnReceipt } 
        }, function (err, r) { 
          if (err) { 
           return next(err); 
          } else { 
           return next(); 
          } 
         }); 
    } 

而且因为它目前的立场与上述功能,我的路线,它利用这个中间件的开始是这样的:

router.post('/doTransaction', 
     validateFields, 
     getNextID, 
     createTransaction, 
     processTransaction, 
     updateDatabase, 
     function(req, res, next) { //... 

好像我应该是能够创建一个中间件函数,它可以连续执行所有这些操作,而不必每个中间件都是单独的中间件,但是由于每个中间件都有一个异步函数,因此我需要在结果回调中调用next(),这是只有这样我才能看到它的工作。

感谢 亚伦

+0

你好,如果你在这里发布一些代码会很好 – Nicolai 2015-04-02 12:52:18

回答

0

感谢尼古拉和robertklep的答案。虽然我认为这两个答案都能回答这个问题,但我意识到自己正在通过这个工作,我没有看到树林里的森林。

我只能通过每个回调函数传递下一个函数,直到我到达最后一个函数并调用它来将控制权交还给中间件堆栈。这也允许我简单地在这些函数内部调用next(err)。

所以我的回答非常类似于Nicolai概述的概念,除非我不认为我需要在这种情况下使用异步包,因为我不觉得这种特殊情况使我回到地狱。

这里是我的回答我自己的问题:

function validateFields(req, res, next) { 
    //... 
    //iterate over req.body to confirm all fields provided 
    //... 
    if (allDataProvided) { 
     //... 
     //iterate over req.body to confirm all fields valid 
     //... 
     if (allDataValid) { 
      getNextID(req, res, next) 
     } else { 
      return(next(err)); 
     } 
    } else { 
     return(next(err)); 
    } 
}, 

//get an auto incrementing ID fields from a mongodb collection (counters) 
function getNextID(req, res, next) { 
    counters.findAndModify(
     { _id: "receiptid" }, 
     [['_id','asc']], 
     { $inc: { seq: 1 } }, 
     {}, 
     function(err, doc) { 
      if (err) { 
       return next(err); 
      } else { 
       req.receiptid = doc.seq; 
       createTransaction(req, res, next); 
      } 
     }); 
}, 

//insert a new record into the transaction collection (txns) using the new ID 
function createTransaction(req, res, next) { 
    txns.insert(
     { _id : req.receiptid, 
      body : req.body, 
      status : "pending"}, 
      {}, 
      function(err, r) { 
      if (err) { 
       return next(err); 
      } else { 
       processTransaction(req, res, next); 
      } 
     }); 
}, 

//process the data on the remote web service using the provider's API (remoteapi) 
function processTransaction(req, res, next) { 
    remoteapi.processTransaction(
     { data: req.body, 
      receiptid: req.receiptid }, 
      function(err, r) { 
      if (err) { 
       return next(err); 
      } else { 
       req.txnReceipt = r; 
       updateDatabase(req, res, next); 
      } 
     }); 
}, 

//update the record in the database collection (txns) with the server response 
function updateDatabase(req, res, next) { 
    txns.updateById(req.receiptid, 
       { $set :{status : "success", 
         receipt: req.txnReceipt } 
       }, function (err, r) { 
         if (err) { 
          return next(err); 
         } else { 
          return next(); 
         } 
        }); 
} 

所以不是调用next()上的每个异步函数成功完成,不得不另写中间件下一步,我只是通过下一对到下一个功能,直到它是必需的。

这是,我可以只调用第一个函数作为我的中间件,像这样:

router.post('/doTransaction', 
     validateFields, 
     function(req, res, next) { //... 

反过来,每个动作完成时剩余的步骤都按顺序调用。

1

这是非常容易实现的一个中间件所有的步骤。我在下面列出了一些伪代码(它对你的代码的结构做了各种假设,因为你没有提供实现细节,但它只是给出一个想法)。

它使用on-headers包来“捕捉”响应。

var onHeaders = require('on-headers') 

// Your middleware function 
app.use(function(req, res, next) { 

    // Update the database when the response is being sent back. 
    onHeaders(res, function() { 
    // Do database update if we have a document id. 
    if (req._newDocumentId) { 
     db.collection.update(req._newDocumentId, data, function() { 
     // can't do a lot here! 
     }); 
    } 
    }); 

    // Perform the requires steps 
    if (! checkValuesAreSupplied(req)) { 
    return next(new Error(...)); 
    } 

    if (! validateValues(req)) { 
    return next(new Error(...)); 
    } 

    // Insert into database. 
    db.collection.insert(data, function(err, doc) { 
    if (err) return next(err); 

    ...process the newly created doc... 

    // Store _id in the request for later. 
    req._newDocumentId = doc._id; 

    // Make the call to the separate server 
    makeCallToOtherServer(otherData, function(err, response) { 
     if (err) return next(err); 

     ...process response... 

     return next(); 
    }); 
    }); 
}); 
1

你可以把一切都在一个模块中,只需使用回调去trought每一个步骤,但在这种情况下,你可以得到“回调地狱”

所以我可以建议async npm package我认为这是更好的方法。使用这个库

您的代码如下:

function allInOneMiddleware(req, res, next) { 
    async.waterfall([ 
     function (callback) { 
      validateFields(req, res, callback); 
     }, 
     getNextID, 
     createTransaction, 
     processTransaction, 
     updateDatabase 
    ], function (err) { 
     if (err) { 
      return next(err); 
     } 
     // response? 
    }); 
} 

function validateFields(req, res, callback) { 
    //... 
    //iterate over req.body to confirm all fields provided 
    //... 
    if (allDataProvided) { 
     //... 
     //iterate over req.body to confirm all fields valid 
     //... 
     if (allDataValid) { 
      return callback(null, req.body); 
     } 
     return callback(err); 
    } 
    return callback(err); 
} 

//get an auto incrementing ID fields from a mongodb collection (counters) 
function getNextID(body, callback) { 
    counters.findAndModify(
     {_id: "receiptid"}, 
     [['_id', 'asc']], 
     {$inc: {seq: 1}}, 
     {}, 
     function (err, doc) { 
      if (err) { 
       return callback(err); 
      } 
      callback(null, body, doc.seq); 
     }); 
} 

//insert a new record into the transaction collection (txns) using the new ID 
function createTransaction(body, receiptid, callback) { 
    txns.insert(
     { 
      _id: receiptid, 
      body: body, 
      status: "pending" 
     }, 
     {}, 
     function (err, r) { 
      if (err) { 
       return callback(err); 
      } 
      callback(null, body, receiptid); 
     }); 
} 

//process the data on the remote web service using the provider's API (remoteapi) 
function processTransaction(body, receiptid, callback) { 
    remoteapi.processTransaction(
     { 
      data: body, 
      receiptid: receiptid 
     }, 
     function (err, r) { 
      if (err) { 
       return callback(err); 
      } 
      callback(null, receiptid, r); 
     }); 
} 

//update the record in the database collection (txns) with the server response 
function updateDatabase(receiptid, txnReceipt, callback) { 
    txns.updateById(receiptid, 
     { 
      $set: { 
       status: "success", 
       receipt: txnReceipt 
      } 
     }, callback); 
}