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.