Expresso: Como passar instância de aplicativo para rotas de um arquivo diferente?


103

Quero dividir minhas rotas em arquivos diferentes, onde um arquivo contém todas as rotas e o outro as ações correspondentes. Atualmente tenho uma solução para conseguir isso, porém preciso tornar a instância do aplicativo global para poder acessá-la nas ações. Minha configuração atual é assim:

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');
});

routes.js:

exports.setup = function(app, controllers) {

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

};

controladores / index.js:

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

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

controladores / posts.js:

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

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

No entanto, essa configuração tem um grande problema: eu tenho um banco de dados e uma instância de aplicativo que preciso passar para as ações (controladores / *. Js). A única opção que eu poderia pensar é tornar as duas variáveis ​​globais, o que não é realmente uma solução. Quero separar as rotas das ações porque tenho muitas rotas e as quero em um local central.

Qual é a melhor maneira de passar variáveis ​​para as ações, mas separar as ações das rotas?


Como é o seu controllers.js? Talvez você possa torná-lo uma função (em vez de um objeto) que pode receber parâmetros.
— mihai

require ('controladores') requer controladores / index.js. No entanto, uma função não funciona porque eu uso o objeto nas rotas (consulte routes.js) e, portanto, não posso passar argumentos para ele, mesmo que seja uma função.
— Claudio Albertin

Respostas:


165

Use req.app,req.app.get('somekey')

A variável do aplicativo criada pela chamada express()é definida nos objetos de solicitação e resposta.

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


Obrigado. Acho que essa é a melhor maneira de acessar variáveis ​​definidas com app.set ('name', val);
— Pavel Kostenko

4
Não se esqueça de chamar app.set('somekey', {})app.js
— ankitjaininfo

3
Minha única reclamação sobre esse caminho, embora eu adore, é que quando você está tentando executar um app.locals.authorized como tal (não em main.js): app.route('/something').get(app.locals.authorized,function(req,res,next){});não é possível porque está fora do escopo de solicitação.
— gabeio

Estou usando uma estratégia de passaporte diferente para um parâmetro de consulta diferente. Portanto, estou tentando definir passport.use ("nome-da-estratégia") em um middleware. Mesmo se eu armazenar o passaporte nesse middleware apenas com let passport = req.app, get ('passport'). Ele está sendo modificado para outro conjunto de solicitações. Por que é tão ?
— Kartikeya Mishra

Se eu fizer isso, o objeto req terá instâncias adicionais de Object como redis e db no meu caso. Isso não afetará o desempenho do aplicativo? por exemplo: em index.js app.set ('redis', redis_client); em routes / example.js router = require ('express'). Router (); route.get ('/ test', (req, res, next) => {conosle.log (req.app.get ('redis')); return res.send ("// feito");})
— Suz Aann Shrestha

101

Node.js oferece suporte a dependências circulares.
Usar dependências circulares em vez de require ('./ routes') (app) limpa uma grande quantidade de código e torna cada módulo menos interdependente de seu arquivo de carregamento:


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


rotas / 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');


----- Atualização de 04/2014 ----- O
Express 4.0 corrigiu o caso de uso para definir rotas adicionando um método express.router ()!
documentação - http://expressjs.com/4x/api.html#router

Exemplo de seu novo gerador:
Escrevendo a rota:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js
Adicionando / namespacing ao aplicativo: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

Ainda existem casos de uso para acessar o aplicativo de outros recursos, portanto, dependências circulares ainda são uma solução válida.


1
"menos interdependente de seu arquivo de carregamento" - depende do caminho de arquivo específico de seu arquivo de carregamento. Isso é um acoplamento muito forte, então não vamos fingir que não é.
— Camilo Martin,

2
Basta ter muito cuidado (leia: não faça o que tenho lutado na última hora +) para app.jssolicitar o arquivo de roteamento após exportar o aplicativo. require()Chamadas circulares podem ser uma verdadeira bagunça, então certifique-se de saber como elas funcionam !
— Nateowami

Sinceramente, acho que a resposta de @Feng sobre o uso de req.app.get ('somekey') é de fato uma solução muito melhor e mais limpa do que usar dependências circulr.
— Claudio Mezzasalma

@Green se o aplicativo estiver vazio, você precisará de um arquivo que seja necessário appANTES do aplicativo module.exportsser definido. Você tem que instanciar app, definir e module.exports, em seguida, exigir arquivos que podem exigir. app Mas de qualquer maneira, fazer as dependências circulares é um antipadrão que o express resolveu - você não deve precisar mais fazer isso.
— Will Stern

26

Como disse nos comentários, você pode usar uma função como module.exports. Uma função também é um objeto, então você não precisa alterar sua sintaxe.

app.js

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

controllers.js

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

controladores / index.js

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

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

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

module.exports = controllers;

É correto retornar um objeto dentro da função ou é melhor definir os métodos da maneira que você fez no seu exemplo?
— Claudio Albertin

Eu acho que qualquer uma das abordagens está ok.
— mihai

Como tenho muitos métodos, prefiro defini-los como um objeto em vez de cada um manualmente. Isso funcionaria quando eu apenas retornasse o objeto, mas não há uma solução um pouco mais plana? Meus métodos reais seriam indentados duas vezes ...
— Claudio Albertin

Não tenho certeza se entendi você, mas acho que você poderia mover a implementação para fora dessa controllersfunção, algo como: jsfiddle.net/mihaifm/yV79K
— mihai

os controladores / index.js não precisam retornar os controladores var?
— Yalamber

5

Ou apenas faça isso:

var app = req.app

dentro do Middleware que você está usando para essas rotas. Curtiu isso:

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

Alguém me diga por que essa não é a resposta aceita? Para dependências que você usa app.use('my-service', serviceInstance)no roteador principal e req.app.get('my-service')no controlador conforme mencionado por @Feng
— Felipe

0

Digamos que você tenha uma pasta chamada "contollers".

Em seu app.js, você pode colocar este código:

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]");

... e ...

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

em seus controladores anteriores você terá o arquivo "ping.js" com este código:

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

E é isso ....


0
  1. Para tornar seu objeto db acessível a todos os controladores sem passá-lo para todos os lugares: faça um middleware de nível de aplicativo que anexa o objeto db a cada objeto req, então você pode acessá-lo em cada controlador.
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. para evitar passar instância de aplicativo em todos os lugares, em vez disso, passar rotas para onde o aplicativo está
// 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

O app.js final:

// 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');
});

Outra versão: você pode personalizar de acordo com suas necessidades, como adicionar solicitação de postagem

// 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

Para banco de dados separado, serviço de acesso a dados que fará todo o trabalho do banco de dados com API simples e evitará estado compartilhado.

Separar routes.setup parece uma sobrecarga. Eu preferiria colocar um roteamento baseado em configuração em vez disso. E configure rotas em .json ou com anotações.


O que você quer dizer com serviço de acesso a dados? Como ficaria?
— Claudio Albertin

Meu arquivo routes.js real é muito maior e usa o módulo express-namespaces. Como você separaria as rotas das ações?
— Claudio Albertin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.