Express: come passare l'istanza dell'app alle route da un file diverso?


103

Voglio suddividere i miei percorsi in diversi file, dove un file contiene tutti i percorsi e l'altro le azioni corrispondenti. Al momento ho una soluzione per raggiungere questo obiettivo, tuttavia devo rendere globale l'istanza dell'app per potervi accedere nelle azioni. La mia configurazione attuale è simile a questa:

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.

};

controller / index.js:

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

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

controller / posts.js:

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

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

Tuttavia, questa configurazione ha un grosso problema: ho un database e un'istanza di app che devo passare alle azioni (controller / *. Js). L'unica opzione a cui potrei pensare è rendere globali entrambe le variabili, il che non è davvero una soluzione. Voglio separare le rotte dalle azioni perché ne ho molte e le voglio in una posizione centrale.

Qual è il modo migliore per passare le variabili alle azioni ma separare le azioni dai percorsi?


Come appare il tuo controller.js? Forse puoi renderlo una funzione (invece di un oggetto) che può ricevere parametri.
mihai

require ('controllers') richiede controllers / index.js. Tuttavia, una funzione non funzionerà perché utilizzo l'oggetto nelle rotte (vedi route.js) e quindi non posso passarle argomenti, anche se è una funzione.
Claudio Albertin

Risposte:


165

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

La variabile dell'applicazione creata chiamando express()viene impostata sugli oggetti richiesta e risposta.

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


Grazie. Penso che questo sia il modo migliore per accedere alle variabili impostate con app.set ('name', val);
Pavel Kostenko

4
Non dimenticare di chiamare app.set('somekey', {})app.js
ankitjaininfo

3
La mia unica lamentela su questo modo, anche se mi piace, è che quando stai cercando di eseguire un app.locals.authorized come tale (non in main.js): app.route('/something').get(app.locals.authorized,function(req,res,next){});non è possibile perché è al di fuori dell'ambito req.
gabeio

Sto usando una diversa strategia del passaporto per diversi parametri di query. Quindi sto cercando di impostare passport.use ("nome-strategia") in un middleware. Anche se memorizzo passport in quel middleware solo con let passport = req.app, get ('passport'). È in fase di modifica per un'altra serie di richieste. Perché è così ?
Kartikeya Mishra

Se lo faccio, nel mio caso l'oggetto req avrà istanze di oggetti aggiuntive come redis e db. Non influirà sulle prestazioni dell'applicazione? ad esempio: in index.js app.set ('redis', redis_client); in route / 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 supporta le dipendenze circolari.
Facendo uso di dipendenze circolari invece di require ('./ routes') (app) pulisce molto codice e rende ogni modulo meno interdipendente dal suo file di caricamento:


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


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


----- Aggiornamento 04/2014 -----
Express 4.0 ha corretto il caso d'uso per la definizione delle rotte aggiungendo un metodo express.router ()!
documentazione - http://expressjs.com/4x/api.html#router

Esempio dal loro nuovo generatore:
Scrittura del percorso:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js
Aggiunta / spaziatura dei nomi all'app: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

Ci sono ancora casi d'uso per accedere all'app da altre risorse, quindi le dipendenze circolari sono ancora una soluzione valida.


1
"meno interdipendente dal file di caricamento": dipende dal percorso file specifico del file di caricamento. È un accoppiamento molto stretto, quindi non fingiamo che non lo sia.
Camilo Martin,

2
Fai solo molta attenzione (leggi: non fare ciò con cui ho lottato nell'ultima ora +) che app.jsrichiedi il file di instradamento dopo aver esportato l'app. Le require()chiamate circolari possono creare un vero disastro, quindi assicurati di sapere come funzionano !
Nateowami

Onestamente penso che la risposta di @Feng sull'uso di req.app.get ('somekey') sia davvero una soluzione migliore e più pulita rispetto all'utilizzo delle dipendenze circulr.
Claudio Mezzasalma

@ Verde se l'app è vuota, hai richiesto un file che richiedeva appPRIMA che l'app module.exportsfosse definita. Devi istanziare app, impostare module.exports, quindi richiedere file che potrebbero richiedere app Ma in ogni caso, fare le dipendenze circolari è un anti-pattern che Express ha risolto - non dovresti più aver bisogno di farlo.
Will Stern il

26

Come ho detto nei commenti, puoi usare una funzione come module.exports. Una funzione è anche un oggetto, quindi non è necessario modificare la sintassi.

app.js

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

controllers.js

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

controllori / index.js

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

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

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

module.exports = controllers;

Va bene restituire un oggetto all'interno della funzione o è meglio quindi impostare i metodi come si fa nell'esempio?
Claudio Albertin

Penso che entrambi gli approcci siano ok.
mihai

Poiché ho molti metodi, preferirei impostarli come oggetto anziché ciascuno manualmente. Questo funzionerebbe quando restituisco l'oggetto, ma non c'è una soluzione un po 'più piatta? I miei metodi effettivi sarebbero rientrati due volte ...
Claudio Albertin

Non sono sicuro di averti capito, ma immagino che potresti spostare l'implementazione al di fuori di quella controllersfunzione, qualcosa come: jsfiddle.net/mihaifm/yV79K
mihai

controllers / index.js non deve restituire i controller var?
Yalamber

5

O fallo semplicemente:

var app = req.app

all'interno del middleware che stai utilizzando per queste rotte. Come quello:

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

Qualcuno mi dica perché questa non è la risposta accettata? Per le dipendenze che usi app.use('my-service', serviceInstance)nel router principale e req.app.get('my-service')nel controller come menzionato da @Feng
Felipe

0

Diciamo che hai una cartella chiamata "contollers".

Nel tuo app.js puoi inserire questo codice:

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

nei tuoi controller più vecchi avrai il file "ping.js" con questo codice:

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

E questo è tutto ...


0
  1. Per rendere il tuo oggetto db accessibile a tutti i controller senza passarlo ovunque: crea un middleware a livello di applicazione che colleghi l'oggetto db a ogni oggetto req, quindi puoi accedervi all'interno di ogni controller.
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. per evitare di passare l'istanza dell'app ovunque, invece, passando i percorsi dove si trova l'app
// 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

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

Un'altra versione: puoi personalizzarla in base alle tue esigenze, come l'aggiunta di una richiesta di post

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

Per il database separare il servizio di accesso ai dati che farà funzionare tutto il DB con una semplice API ed eviterà lo stato condiviso.

Separare route.setup sembra sovraccarico. Preferirei invece inserire un routing basato sulla configurazione. E configura le rotte in .json o con annotazioni.


Cosa intendi con un servizio di accesso ai dati? Come sarebbe?
Claudio Albertin

Il mio vero file routes.js è molto più grande e usa il modulo express-namespaces. Come separereste i percorsi dalle azioni?
Claudio Albertin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.