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?


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


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.


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;

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

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


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

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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.