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.