如何仅将参数传递给需要它的中间件?

How to pass parameter only to middleware that need it?

我有一个 Express 应用程序,我正试图将我所有的中间件放在它自己的文件中。有些中间件函数需要 db 对象,有些则不需要。

对于不需要 db 对象的函数来说非常简单,但是鉴于我下面的代码结构,我如何在 doesNotNeedDbParam 中引用 db 对象,因为它已经有参数 reqresnext?

somefile.js:

const router = express.Router()
const doesNotNeedDbParam = require('./middleware')().doesNotNeedDbParam

function foo () {
  // Currently I have to call require and pass in the db object here b/c 
  // it's not set when requiring the function doesNotNeedDbParam
  router.use(require('./middleware')(db).needsDbParam // <-- Is there a better way to do this so that I can require the file above and pass the db object in when it's set?
}

// Setup db object here
foo()

middleware.js

function doesNotNeedDbParam (req, res, next) {
  ...
}

function needsDbParam (req, res, next) {
  // Where do I reference the db variable?
}

module.exports = (db) => {
  return {
    doesNotNeedDbParam: doesNotNeedDbParam,
    needsDbParam: needsDbParam
  }
}

为什么不将 module.exports 声明为单个函数:

module.exports = (db) => { 
    let module = {}; 

    module.doesNotNeedDbParam = (req, res) => {
        // Do Stuff
    };

    module.needsDbParam = (req, res) => { 
        // db now in scope
    };

    return module;
};

这就是你的 somefile.js 会变成的样子:

const router = express.Router();
const db = initializeDb();

const doesNotNeedDbParam = require('./middleware')().doesNotNeedDbParam;
router.use(require('./middleware')(db).needsDbParam);

您也可以这样设置一次:

const middleware = require('./middleware')(db);
const doesNotNeedParam = middleware.doesNotNeedParam;
router.use(middleware.needsDbParam);

这与您之前所做的并没有什么不同,但现在您可以访问 needsDbParam 中的数据库。如果您的 initializeDb 函数是异步的,那么您将需要使用 Promise 或其他一些异步库来包含数据库设置后。

函数方法

我认为一个好的结构是尝试currying your middleware. This is a pattern practiced by middleware such as body-parser and internally by Express itself with serve-static。这样,你只需要一次,并在你需要的地方传递 db,而不是在你不需要的地方:

// Instead of declaring each function directly as a middleware function,
// we declare them as a function that returns a middleware function
function doesNotNeedDbParam () {
  return function (req, res, next) {
    …
  }
}

function needsDbParam (db) {
  return function (req, res, next) {
    // You can use db here along with req, res, next
  }
}

// No need to export a function now
module.exports = {
  doesNotNeedDbParam,
  needDbParam,
};

那么,只需要:

const middleware = require('./middleware');
…
router.use(middleware.doesNotNeedDbParam()); // Since this doesn't need anything, no argument
router.use(middleware.needsDbParam(db)); // You can pass db here now

如果您熟悉 ES6+ 语法,您可以压缩为:

const doesNotNeedDbParam = () => (req, res, next) => {
  …
}

const needsDbParam = (db) => (req, res, next) => {
  // Use db here
}
// Export object here...

然后:

const { doesNotNeedDbParam, needsDbParam } = require('./middleware');
… 
router.use(doesNotNeedDbParam());
router.use(needsDbParam(db));

附加方法

还有另一种方法可以做到这一点,方法是将 属性 附加到 req 对象 once。这消除了每次需要时重新传递 db 的需要。许多其他包使用此策略。它是这样的:

function attachDb (db) { // Still use curry approach here since we want db
  return function (req, res, next) {
    // Attaches the specified db to req directly
    req.db = db;
  }
}

function needsDbParam (req, res, next) { // No need for currying
  // Now use req.db here
}
// Export your other middleware…

然后,像这样使用它,确保 attachDbfirst 以便在使用它之前分配 属性:

router.use(attachDb(db)); // Before all other middleware that depend on req.db
…
// No need to call because this is already the middleware function, 
// able to use req.db, which was assigned via attachDb
router.use(needDbParam);