快递:如何将应用实例传递给来自其他文件的路由?


103

我想将路由分成不同的文件,其中一个文件包含所有路由,另一个文件包含相应的操作。我目前有一个解决方案来实现此目的,但是我需要使应用程序实例成为全局实例,以便能够在操作中对其进行访问。我当前的设置如下所示:

app.js:

var express   = require('express');
var app       = express.createServer();
var routes    = require('./routes');

var controllers = require('./controllers');
routes.setup(app, controllers);

app.listen(3000, function() {
  console.log('Application is listening on port 3000');
});

route.js:

exports.setup = function(app, controllers) {

  app.get('/', controllers.index);
  app.get('/posts', controllers.posts.index);
  app.get('/posts/:post', controllers.posts.show);
  // etc.

};

控制器/ index.js:

exports.posts = require('./posts');

exports.index = function(req, res) {
  // code
};

控制器/posts.js:

exports.index = function(req, res) {
  // code
};

exports.show = function(req, res) {
  // code
};

但是,此设置存在一个大问题:我需要传递给操作(controllers / *。js)的数据库和应用程序实例。我能想到的唯一选择是将两个变量都设置为全局变量,这并不是真正的解决方案。我想将路线与操作分开,因为我有很多路线,并且希望将它们放在中央位置。

将变量传递到动作但将动作与路线分开的最佳方法是什么?


您的controllers.js看起来如何?也许您可以使其成为可以接收参数的函数(而不是对象)。
mihai 2012年

require('controllers')需要controllers / index.js。但是,一个函数将不起作用,因为我在路由中使用了该对象(请参阅routes.js),因此即使它是一个函数,也无法将参数传递给该对象。
克劳迪奥·艾伯丁

Answers:


165

使用req.appreq.app.get('somekey')

通过调用创建的应用程序变量在express()请求和响应对象上设置。

参见:https : //github.com/visionmedia/express/blob/76147c78a15904d4e4e469095a29d1bec9775ab6/lib/express.js#L34-L35


谢谢。我认为这是访问使用app.set('name',val);设置的变量的最佳方法;
Pavel Kostenko

4
不要忘记调用app.set('somekey', {})app.js
ankitjaininfo 2014年

3
尽管我确实喜欢它,但我对这种方式的唯一抱怨是,当您尝试运行具有这样的权限(不在main.js中)的app.locals.author:app.route('/something').get(app.locals.authorized,function(req,res,next){});是不可能的,因为它超出了req范围。
gabeio'1

我对不同的查询参数使用了不同的通行证策略。所以我试图在中间件中设置passport.use(“策略名称”)。即使我仅使用let password = req.app,get('passport')将护照存储在该中间件中。正在针对另一组请求对其进行修改。为什么会这样呢?
Kartikeya Mishra

如果我这样做,那么在我的情况下,req对象将具有其他对象实例,例如redis和db。它不会影响应用程序性能吗?例如:在index.js app.set('redis',redis_client);中 在routes / example.js中router = require('express')。Router(); route.get('/ test',(req,res,next)=> {conosle.log(req.app.get('redis')); return res.send(“ // done”);})
Suz Aann shrestha

101

Node.js支持循环依赖。
利用循环依赖而不是require('./ routes')(app)清除了大量代码,并使每个模块减少与其加载文件的依赖:


app.js

var app = module.exports = express(); //now app.js can be required to bring app into any file

//some app/middleware setup, etc, including 
app.use(app.router);

require('./routes'); //module.exports must be defined before this line


路线/index.js

var app = require('../app');

app.get('/', function(req, res, next) {
  res.render('index');
});

//require in some other route files...each of which requires app independently
require('./user');
require('./blog');


----- 2014年4月更新-----
Express 4.0通过添加express.router()方法修复了用于定义路由的用例!
文档-http://expressjs.com/4x/api.html#router

新生成器的示例:
编写路由:
https : //github.com/expressjs/generator/blob/master/templates/js/routes/index.js
将其添加/命名到应用中:https : //github.com /expressjs/generator/blob/master/templates/js/app.js#L24

仍然存在从其他资源访问应用程序的用例,因此循环依赖关系仍然是有效的解决方案。


1
“较少依赖其加载文件”-它依赖于其加载文件的特定文件路径。这是非常紧密的耦合,所以我们不要假装不是这样。
卡米洛·马丁

2
请非常小心(请阅读:不要做过去一个多小时来我一直在努力的工作),在导出应用程序app.js您需要路由文件。循环调用可能会造成混乱,因此请确保您知道它们的工作原理require()
Nateowami 2015年

老实说,我认为@Feng给出的关于使用req.app.get('somekey')的答案确实比使用circulr依赖关系更好,更清洁。
克劳迪奥·梅扎萨玛

@绿色(如果应用为空),则需要一个定义了app应用之前的文件module.exports。您必须实例化app,set module.exports,然后需要可能需要的文件,app 但是无论哪种方式,执行循环依赖关系都是表达已解决的反模式-您不再需要这样做。
Will Stern

26

就像我在评论中所说的,您可以将一个函数用作module.exports。函数也是对象,因此您不必更改语法。

app.js

var controllers = require('./controllers')({app: app});

controllers.js

module.exports = function(params)
{
    return require('controllers/index')(params);
}

控制器/ index.js

function controllers(params)
{
  var app = params.app;

  controllers.posts = require('./posts');

  controllers.index = function(req, res) {
    // code
  };
}

module.exports = controllers;

是否可以在函数内返回对象,还是更好,所以请按照示例中的方法设置方法?
克劳迪奥·艾伯丁

我认为这两种方法都可以。
mihai 2012年

因为我有很多方法,所以我宁愿将它们设置为一个对象,而不是手动设置每个对象。当我刚刚返回对象时,这将起作用,但是没有一个更讨人喜欢的解决方案吗?我的实际方法会两次缩进...
克劳迪奥·阿尔伯丁2012年

不知道我是否理解您,但是我想您可以将实现移至该controllers函数之外,例如:jsfiddle.net/mihaifm/yV79K
mihai 2012年

controllers / index.js是否不需要返回控制器var?
2013年

5

或者只是这样做:

var app = req.app

在用于这些路由的中间件中。像那样:

router.use( (req,res,next) => {
    app = req.app;
    next();
});

有人告诉我为什么这不是公认的答案?对于依赖性,您可以app.use('my-service', serviceInstance)在主路由器和req.app.get('my-service')控制器中使用@Feng
Felipe,

0

假设您有一个名为“ contollers”的文件夹。

在您的app.js中,您可以输入以下代码:

console.log("Loading controllers....");
var controllers = {};

var controllers_path = process.cwd() + '/controllers'

fs.readdirSync(controllers_path).forEach(function (file) {
    if (file.indexOf('.js') != -1) {
        controllers[file.split('.')[0]] = require(controllers_path + '/' + file)
    }
});

console.log("Controllers loaded..............[ok]");

...和...

router.get('/ping', controllers.ping.pinging);

在您的控制器中,您将获得带有以下代码的文件“ ping.js”:

exports.pinging = function(req, res, next){
    console.log("ping ...");
}

就是这样...


0
  1. 要使您的db对象可以被所有控制器访问而不将其传递到任何地方,请执行以下操作:制作一个将db对象附加到每个req对象的应用程序级中间件,然后可以在每个控制器中访问它。
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. 以避免将应用程序实例传递到任何地方,而是将路由传递到应用程序所在的位置
// routes.js  It's just a mapping.
exports.routes = [
  ['/', controllers.index],
  ['/posts', controllers.posts.index],
  ['/posts/:post', controllers.posts.show]
];

// app.js
var { routes }    = require('./routes');
routes.forEach(route => app.get(...route));
// You can customize this according to your own needs, like adding post request

最终的app.js:

// app.js
var express   = require('express');
var app       = express.createServer();

let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);

var { routes }    = require('./routes');
routes.forEach(route => app.get(...route));

app.listen(3000, function() {
  console.log('Application is listening on port 3000');
});

另一个版本:您可以根据自己的需要对此进行自定义,例如添加发布请求

// routes.js  It's just a mapping.
let get = ({path, callback}) => ({app})=>{
  app.get(path, callback);
}
let post = ({path, callback}) => ({app})=>{
  app.post(path, callback);
}
let someFn = ({path, callback}) => ({app})=>{
  // ...custom logic
  app.get(path, callback);
}
exports.routes = [
  get({path: '/', callback: controllers.index}),
  post({path: '/posts', callback: controllers.posts.index}),
  someFn({path: '/posts/:post', callback: controllers.posts.show}),
];

// app.js
var { routes }    = require('./routes');
routes.forEach(route => route({app}));

-1

对于数据库,请单独使用数据访问服务,该服务将使用简单的API来完成所有数据库的工作,并避免共享状态。

分隔route.setup看起来像是开销。我宁愿放置基于配置的路由。并在.json或带有注释中配置路由。


您对数据访问服务是什么意思?看起来如何?
克劳迪奥·艾伯丁

我真正的routes.js文件更大,并使用express-namespaces模块。您如何将路线与动作分开?
克劳迪奥·艾伯丁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.