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


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.