Express: Jak przekazać instancję aplikacji do tras z innego pliku?

104

Chcę podzielić moje trasy na różne pliki, z których jeden zawiera wszystkie trasy, a drugi odpowiednie akcje. Obecnie mam rozwiązanie, aby to osiągnąć, jednak muszę ustawić instancję aplikacji jako globalną, aby mieć do niej dostęp w akcjach. Moja obecna konfiguracja wygląda następująco:

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.

};

controllers / index.js:

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

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

controllers / posts.js:

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

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

Jednak ta konfiguracja ma duży problem: mam bazę danych i instancję aplikacji, które muszę przekazać do akcji (controllers / *. Js). Jedyną opcją, o której mógłbym pomyśleć, jest uczynienie obu zmiennych globalnymi, co nie jest rozwiązaniem. Chcę oddzielić trasy od działań, ponieważ mam dużo tras i chcę, aby były w centralnym miejscu.

Jaki jest najlepszy sposób przekazywania zmiennych do akcji, ale oddzielając działania od tras?

Claudio Albertin
źródło
Jak wygląda twój controller.js? Może mógłbyś uczynić z tego funkcję (zamiast obiektu), która może otrzymywać parametry.
mihai
require ('controllers') wymaga controllers / index.js. Jednak funkcja nie zadziała, ponieważ używam tego obiektu w trasach (patrz route.js) i dlatego nie mogę przekazywać do niego argumentów, nawet jeśli jest to funkcja.
Claudio Albertin

Odpowiedzi:

166

Użyj req.app,req.app.get('somekey')

Zmienna aplikacji utworzona przez wywołanie express()jest ustawiana w obiektach żądania i odpowiedzi.

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

Feng
źródło
Dzięki. Myślę, że jest to najlepszy sposób na dostęp do zmiennych ustawionych za pomocą app.set ('name', val);
Pavel Kostenko,
4
Nie zapomnij zadzwonić app.set('somekey', {})do app.js
ankitjaininfo
3
Moje jedyne zastrzeżenie co do tego sposobu, chociaż bardzo mi się podoba, to to, że kiedy próbujesz uruchomić app.locals.autoryzowany jako taki (nie w main.js): app.route('/something').get(app.locals.authorized,function(req,res,next){});nie jest to możliwe, ponieważ jest poza zakresem wymagań .
gabeio,
Używam innej strategii paszportu dla różnych parametrów zapytania. Więc próbuję ustawić passport.use ("nazwa-strategii") w oprogramowaniu pośrednim. Nawet jeśli przechowuję paszport w tym oprogramowaniu pośrednim tylko z let passport = req.app, get ('paszport'). Jest modyfikowany dla innego zestawu żądań. Dlaczego tak się dzieje?
Kartikeya Mishra
Jeśli to zrobię, obiekt req będzie miał dodatkowe wystąpienia Object, takie jak redis i db w moim przypadku. Czy nie wpłynie to na wydajność aplikacji? np .: w index.js app.set ('redis', redis_client); w route / example.js router = require ('express'). Router (); route.get ('/ test', (req, res, next) => {conosle.log (req.app.get ('redis')); return res.send ("// gotowe");})
Suz Aann shrestha
101

Node.js obsługuje zależności cykliczne.
Wykorzystanie zależności cyklicznych zamiast require ('./ tours') (app) czyści wiele kodu i sprawia, że ​​każdy moduł jest mniej zależny od pliku ładującego:


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


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


----- Aktualizacja 04/2014 -----
Express 4.0 naprawił przypadek użycia przy definiowaniu tras poprzez dodanie metody express.router ()!
dokumentacja - http://expressjs.com/4x/api.html#router

Przykład z ich nowego generatora:
Pisanie trasy:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js
Dodawanie / tworzenie przestrzeni nazw do aplikacji: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

Nadal istnieją przypadki uzyskiwania dostępu do aplikacji z innych zasobów, więc zależności cykliczne są nadal prawidłowym rozwiązaniem.

Will Stern
źródło
1
„mniej współzależne od wczytywanego pliku” - jest zależne od określonej ścieżki pliku ładującego się. To bardzo mocne połączenie, więc nie udawajmy, że tak nie jest.
Camilo Martin,
2
Po prostu bądź bardzo ostrożny (czytaj: nie rób tego, z czym walczyłem przez ostatnią godzinę +), app.jsże potrzebujesz pliku routingu po wyeksportowaniu aplikacji. require()Połączenia okólne mogą wywołać prawdziwy bałagan, więc upewnij się, że wiesz, jak one działają !
Nateowami
Szczerze myślę, że odpowiedź @Feng na temat korzystania z req.app.get ('somekey') jest rzeczywiście lepszym i czystszym rozwiązaniem niż użycie zależności circulr.
Claudio Mezzasalma
@ Zielony Jeśli aplikacja jest pusta, wymagany jest plik, który był wymagany appPRZED module.exportszdefiniowaniem aplikacji . Musisz utworzyć instancję app, ustawić module.exports, a następnie wymagać plików, które mogą wymagać. app Ale tak czy inaczej, wykonywanie zależności cyklicznych jest anty-wzorcem, który został rozwiązany przez express - nie powinieneś już tego robić.
Will Stern
26

Jak powiedziałem w komentarzach, możesz użyć funkcji jako module.exports. Funkcja jest również obiektem, więc nie musisz zmieniać swojej składni.

app.js

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

controllers.js

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

controllers / index.js

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

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

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

module.exports = controllers;
mihai
źródło
Czy zwrócenie obiektu wewnątrz funkcji jest w porządku, czy lepiej jest więc ustawić metody tak, jak w przykładzie?
Claudio Albertin,
Myślę, że każde podejście jest w porządku.
mihai,
Ponieważ mam wiele metod, wolałbym ustawić je jako obiekt, a nie każdą ręcznie. To zadziała, gdy po prostu zwrócę obiekt, ale czy nie ma rozwiązania, które jest nieco bardziej płaskie? Moje rzeczywiste metody byłyby wcięte dwukrotnie ...
Claudio Albertin
Nie wiem, czy ja zrozumiałem, ale myślę, że można przenieść poza implementacji że controllersfunkcja, coś jak: jsfiddle.net/mihaifm/yV79K
Mihai
czy controllers / index.js nie musi zwracać kontrolerów var?
Yalamber
5

Lub po prostu zrób to:

var app = req.app

wewnątrz oprogramowania pośredniczącego, którego używasz dla tych tras. Tak:

router.use( (req,res,next) => {
    app = req.app;
    next();
});
asanchez
źródło
Ktoś mi powie, dlaczego nie jest to akceptowana odpowiedź? Zależności, których używasz app.use('my-service', serviceInstance)w głównym routerze i req.app.get('my-service')kontrolerze, o czym wspomniał @Feng
Felipe,
0

Załóżmy, że masz folder o nazwie „contollers”.

W swoim app.js możesz umieścić ten kod:

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

... i ...

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

w swoich kontrolerach będziesz mieć plik „ping.js” z tym kodem:

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

I to jest to ....

Radu Gheorghies
źródło
0
  1. Aby obiekt db był dostępny dla wszystkich kontrolerów bez przekazywania go wszędzie: utwórz oprogramowanie pośredniczące na poziomie aplikacji, które dołącza obiekt db do każdego obiektu req, a następnie możesz uzyskać do niego dostęp w każdym kontrolerze.
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. aby uniknąć przekazywania instancji aplikacji wszędzie, zamiast tego przekazywać trasy do miejsca, w którym znajduje się aplikacja
// 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

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

Inna wersja: możesz dostosować to do własnych potrzeb, na przykład dodać prośbę o wpis

// 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}));
kalać
źródło
-1

W przypadku bazy danych wyodrębnij usługę dostępu do danych, która będzie wykonywać całą pracę bazy danych z prostym interfejsem API i unikać stanu wspólnego.

Oddzielanie route.setup wygląda jak narzut. Wolałbym zamiast tego umieścić routing oparty na konfiguracji. I skonfiguruj trasy w .json lub z adnotacjami.

Eldar Djafarov
źródło
Co masz na myśli mówiąc o usłudze dostępu do danych? Jak by to wyglądało?
Claudio Albertin
Mój prawdziwy plik route.js jest znacznie większy i używa modułu express-namespaces. Jak oddzieliłbyś trasy od działań?
Claudio Albertin