Експрес: Як передати екземпляр програми маршрутам з іншого файлу?


103

Я хочу розділити свої маршрути на різні файли, де один файл містить усі маршрути, а інший відповідні дії. В даний час у мене є рішення для досягнення цього, однак мені потрібно зробити екземпляр додатка глобальним, щоб мати доступ до нього в своїх діях. Моя поточна установка виглядає приблизно так:

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.

};

контролери / 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
};

Однак у цієї установки є велика проблема: у мене є база даних та екземпляр додатка, який мені потрібно передати дії (контролери / *. Js). Єдиний варіант, про який я міг придумати, - це зробити обидві змінні глобальними, що насправді не є рішенням. Я хочу відокремити маршрути від дій, тому що у мене багато маршрутів і хочу, щоб вони були в центральному місці.

Який найкращий спосіб передавати змінні дії, але відокремити дії від маршрутів?


Як виглядає ваш controllers.js? Можливо, ви можете зробити його функцією (замість об'єкта), яка може приймати параметри.
mihai

вимагати ('контролери') вимагає контролерів / index.js. Однак функція не буде працювати, оскільки я використовую об'єкт у маршрутах (див. Route.js) і тому не можу передавати йому аргументи, навіть якщо це функція.
Клаудіо Альбертін

Відповіді:


165

Використовуйте req.app,req.app.get('somekey')

Змінна програма, створена за допомогою виклику express(), встановлюється на об'єкти запиту та відповіді.

Дивіться: https://github.com/visionmedia/express/blob/76147c78a15904d4e4e469095a29d1bec9775ab6/lib/express.js#L34-L35


Дякую. Я думаю, що це найкращий спосіб отримати доступ до змінних, встановлених за допомогою app.set ('ім'я', val);
Павло Костенко

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

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

Я використовую різну паспортну стратегію для різних параметрів запиту. Тому я намагаюся встановити passport.use ("стратегія-ім'я") в середньому програмному забезпеченні. Навіть якщо я зберігаю паспорт у цьому програмному забезпеченні лише з дозволеним паспортом = req.app, get ("паспорт"). Він змінюється для іншого набору запитів. Чому так?
Kartikeya Mishra

Якщо я це зроблю, то в моєму випадку об'єкт req матиме додаткові екземпляри об'єкта, такі як redis та db. Чи це не вплине на ефективність програми? наприклад: в index.js app.set ('redis', redis_client); у маршрутах / example.js router = Require ('express'). Router (); route.get ('/ test', (req, res, next) => {conosle.log (req.app.get ('redis')); повернути res.send ("// зроблено");})
Suz Aann shrestha

101

Node.js підтримує кругові залежності.
Використовуючи кругові залежності замість вимагати ('./ маршрути') (додаток) очищає багато коду та робить кожен модуль менш взаємозалежним у завантажувальному файлі:


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 оновлення -----
Експрес 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") дійсно є набагато кращим і чистішим рішенням, ніж використання залежностей від circulr.
Клаудіо Меццасальма

@Green, якщо програма порожня, вам потрібен файл, який вимагається appДО ПЕРЕД module.exports. Вам потрібно інстанціювати app, встановлювати module.exports, а потім вимагати файли, які можуть вимагати. app Але в будь-якому випадку, кругова залежність є антидіаграмою, яка виражається вирішеною - вам більше не потрібно цього робити.
Буде Стерн

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функції, щось на зразок: jsfiddle.net/mihaifm/yV79K
mihai

чи не потрібно контролерам / index.js повертати контролери var?
Yalamber

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
Felipe

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

у вашому контролері стардера у вас буде файл "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 та уникати спільного стану.

Розділення маршрутів.setup виглядає як накладні. Я б вважав за краще розмістити маршрутизацію на основі конфігурації. І налаштовуйте маршрути в .json або за допомогою приміток.


Що ви маєте на увазі під службою доступу до даних? Як би це виглядало?
Клаудіо Альбертін

Мій реальний файл route.js набагато більший і використовує модуль просторів імен експрес-імен. Як би ви відокремили маршрути від дій?
Клаудіо Альбертін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.