Express: как передать экземпляр приложения в маршруты из другого файла?


104

Я хочу разбить свои маршруты на разные файлы, где один файл содержит все маршруты, а другой - соответствующие действия. В настоящее время у меня есть решение для этого, однако мне нужно сделать экземпляр приложения глобальным, чтобы иметь к нему доступ в действиях. Моя текущая установка выглядит так:

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.

};

контроллеры / index.js:

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

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

контроллеры / posts.js:

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

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

Однако у этой настройки есть большая проблема: у меня есть база данных и экземпляр приложения, которые мне нужно передать действиям (controllers / *. Js). Единственный вариант, который я мог придумать, - это сделать обе переменные глобальными, что на самом деле не является решением. Я хочу отделить маршруты от действий, потому что у меня много маршрутов и я хочу, чтобы они находились в центре.

Как лучше всего передать переменные действиям, но отделить действия от маршрутов?


Как выглядит ваш controllers.js? Возможно, вы можете сделать его функцией (вместо объекта), которая может получать параметры.
mihai

require ('controllers') требует controllers / index.js. Однако функция не будет работать, потому что я использую объект в маршрутах (см. Routes.js) и поэтому не могу передавать ему аргументы, даже если это функция.
Клаудио Альбертин

Ответы:


167

Использование req.app,req.app.get('somekey')

Переменная приложения, созданная при вызове express(), устанавливается для объектов запроса и ответа.

См. Https://github.com/visionmedia/express/blob/76147c78a15904d4e4e469095a29d1bec9775ab6/lib/express.js#L34-L35


Спасибо. Я думаю, что это лучший способ доступа к переменным, установленным с помощью app.set ('name', val);
Павел Костенко

4
Не забудьте позвонить app.set('somekey', {})в app.js
ankitjaininfo 08

3
Моя единственная претензия к этому способу, хотя мне это нравится, заключается в том, что когда вы пытаетесь запустить app.locals.authorized как таковой (не в main.js): app.route('/something').get(app.locals.authorized,function(req,res,next){});это невозможно, потому что это выходит за рамки области req.
gabeio 06

Я использую другую паспортную стратегию для разных параметров запроса. Итак, я пытаюсь установить password.use ("имя-стратегии") в промежуточном программном обеспечении. Даже если я сохраню паспорт в этом промежуточном программном обеспечении только с let паспорт = req.app, get ('паспорт'). Он модифицируется для другого набора запросов. Почему это так ?
Картикея Мишра,

Если я сделаю это, то в моем случае объект req будет иметь дополнительные экземпляры Object, такие как redis и db. Не повлияет ли это на производительность приложения? например: в index.js app.set ('redis', redis_client); в routes / 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 поддерживает циклические зависимости.
Использование циклических зависимостей вместо require ('./ routes') (app) очищает большой объем кода и делает каждый модуль менее зависимым от своего файла загрузки:


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


маршруты / 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');


----- Обновление от 04/2014 ----- В
Express 4.0 исправлена ​​возможность определения маршрутов путем добавления метода express.router ()!
документация - http://expressjs.com/4x/api.html#router

Пример из их нового генератора:
Написание маршрута:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js
Добавление / размещение его в приложении: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

Есть еще варианты использования для доступа к приложению из других ресурсов, поэтому циклические зависимости по-прежнему являются допустимым решением.


1
«менее взаимозависимое это файл загрузка» - это зависит от конкретного FilePath его загрузка файла. Это очень тесная связь, поэтому не будем притворяться, что это не так.
Камило Мартин

2
Просто будьте очень осторожны (читайте: не делайте того, с чем я боролся в течение последнего часа +), что app.jsвам потребуется файл маршрутизации после экспорта приложения. Круговые require()звонки могут создать настоящий беспорядок, поэтому убедитесь, что вы знаете, как они работают !
Nateowami

Я честно думаю, что ответ от @Feng об использовании req.app.get ('somekey') действительно является более лучшим и более чистым решением, чем использование зависимостей Cycler.
Клаудио Меццасалма 01

@Green, если приложение пусто, вам нужен файл, который требуется appДО того, как приложение module.exportsбыло определено. Вы должны создать экземпляр app, установить module.exports, а затем потребовать файлы, которые могут потребоваться. app Но в любом случае выполнение круговых зависимостей - это анти-шаблон, который был решен express - вам больше не нужно этого делать.
Will Stern

26

Как я уже сказал в комментариях, вы можете использовать функцию как module.exports. Функция также является объектом, поэтому вам не нужно менять синтаксис.

app.js

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

controllers.js

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

контроллеры / index.js

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

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

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

module.exports = controllers;

Можно ли вернуть объект внутри функции или лучше установить методы так, как в вашем примере?
Клаудио Альбертин

Я думаю, что любой подход подходит.
mihai

Поскольку у меня много методов, я бы предпочел установить их как объект, а не каждый вручную. Это сработает, если я просто верну объект, но разве нет более плоского решения? Мои настоящие методы будут иметь двойной отступ ...
Клаудио Альбертин

Не уверен, понял ли я вас, но я думаю, вы могли controllersбы вынести
mihai

разве контроллеры / index.js не должны возвращать контроллеры var?
Яламбер

5

Или просто сделайте это:

var app = req.app

внутри промежуточного программного обеспечения, которое вы используете для этих маршрутов. Как это:

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

Кто-нибудь скажет мне, почему это не принятый ответ? Для зависимостей, которые вы используете app.use('my-service', serviceInstance)в основном маршрутизаторе и req.app.get('my-service')контроллере, как указано @Feng
Фелипе,

0

Допустим, у вас есть папка с именем «contollers».

В свой app.js вы можете поместить этот код:

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

... и ...

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

в вашем контроллере forlder у вас будет файл "ping.js" с таким кодом:

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

И вот оно ....


0
  1. Чтобы сделать ваш объект db доступным для всех контроллеров, не передавая его повсюду: создайте промежуточное программное обеспечение уровня приложения, которое присоединяет объект db к каждому объекту req, после чего вы можете получить к нему доступ в каждом контроллере.
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. чтобы избежать передачи экземпляра приложения повсюду, вместо этого передавайте маршруты туда, где приложение
// 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

Последний 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');
});

Другая версия: вы можете настроить это в соответствии с вашими потребностями, например, добавить почтовый запрос

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

Для базы данных выделите службу доступа к данным, которая будет выполнять всю работу с БД с помощью простого API и избегать общего состояния.

Разделение routes.setup похоже на накладные расходы. Я бы предпочел вместо этого использовать маршрутизацию на основе конфигурации. И настраивайте маршруты в .json или с аннотациями.


Что вы имеете в виду под службой доступа к данным? Как бы это выглядело?
Клаудио Альбертин

Мой настоящий файл routes.js намного больше и использует модуль express-namespaces. Как бы вы отделили маршруты от действий?
Клаудио Альбертин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.