Як включити обробники маршрутів у декілька файлів у Express?


223

У моєму expressдодатку NodeJS у мене є app.jsкілька загальних маршрутів. Потім у wf.jsфайлі я хотів би визначити ще кілька маршрутів.

Як я можу app.jsрозпізнати інші обробники маршрутів, визначені у wf.jsфайлі?

Проста вимога , здається, не працює.


2
перевірити цю відповідь stackoverflow.com/a/38718561/1153703
Бікеш M

Відповіді:


399

Якщо ви хочете розмістити маршрути в окремий файл , наприклад routes.js, ви можете створити routes.jsфайл таким чином:

module.exports = function(app){

    app.get('/login', function(req, res){
        res.render('login', {
            title: 'Express Login'
        });
    });

    //other routes..
}

І тоді ви можете вимагати від нього app.jsпередачі app об'єкта таким чином:

require('./routes')(app);

Подивіться також на ці приклади

https://github.com/visionmedia/express/tree/master/examples/route-separation


18
Власне, автор (TJ Holowaychuck) дає кращий підхід
avetisk

Вирішує проблему маршрутизації для декількох файлів, але функції, визначені у app.js, недоступні в маршрутах.
XIMRX

5
Якщо вам потрібні деякі функції, просто покладіть їх на інший модуль / файл та вимагайте їх і від app.js, і route.js
BFil

2
Я зрозумів, що все чує, але вимагаю ("./ маршрутів") (додаток) цей синтекс підірвати мій розум, чи може хто-небудь сказати мені, що це саме, або в чому полягає використання цього, наскільки я знаю, що проходить його додаток "додаток"
ANinJa

6
Нижче є краща відповідь на це запитання - stackoverflow.com/a/37309212/297939
Димитрій

124

Навіть незважаючи на це старіше запитання, я натрапив сюди, шукаючи рішення подібного питання. Спробувавши деякі рішення тут, я закінчився іншим напрямком і подумав, що додам рішення для всіх, хто опиниться тут.

У виразу 4.x ви можете отримати екземпляр об’єкта маршрутизатора та імпортувати інший файл, який містить більше маршрутів. Ви навіть можете це робити рекурсивно, щоб ваші маршрути імпортували інші маршрути, що дозволяє створювати прості у обслуговуванні шляхи до URL. Наприклад, якщо у мене вже є окремий файл маршруту для моєї кінцевої точки '/ тести' і я хочу додати новий набір маршрутів для '/ тестів / автоматизованих', я можу захотіти розбити ці '/ автоматизовані' маршрути в інший файл, щоб збережіть мій файл '/ test' невеликим та простим в управлінні. Це також дозволяє логічно згрупувати маршрути разом за URL-адресою, що може бути дуже зручно.

Зміст ./app.js:

var express = require('express'),
    app = express();

var testRoutes = require('./routes/tests');

// Import my test routes into the path '/test'
app.use('/tests', testRoutes);

Зміст ./routes/tests.js

var express = require('express'),
    router = express.Router();

var automatedRoutes = require('./testRoutes/automated');

router
  // Add a binding to handle '/test'
  .get('/', function(){
    // render the /tests view
  })

  // Import my automated routes into the path '/tests/automated'
  // This works because we're already within the '/tests' route so we're simply appending more routes to the '/tests' endpoint
  .use('/automated', automatedRoutes);

module.exports = router;

Зміст ./routes/testRoutes/automated.js:

var express = require('express'),
    router = express.Router();

router
   // Add a binding for '/tests/automated/'
  .get('/', function(){
    // render the /tests/automated view
  })

module.exports = router;

2
ця найкраща відповідь, має бути в першу чергу! Дякую
Костанос

чи можу я використовувати цю структуру для Node Js Rest API?
МСМ

@MSMurugan yep u може побудувати api для відпочинку з цим зразком.
ShortRound1911

@ ShortRound1911 Я будую інший api цю схему і розміщую на сервері plesk хостингу, я отримую помилку
MSM

96

Спираючись на приклад @ShadowCloud, я зміг динамічно включати всі маршрути в підкаталог.

маршрути / index.js

var fs = require('fs');

module.exports = function(app){
    fs.readdirSync(__dirname).forEach(function(file) {
        if (file == "index.js") return;
        var name = file.substr(0, file.indexOf('.'));
        require('./' + name)(app);
    });
}

Потім розміщуйте файли маршрутів у каталозі маршрутів так:

маршрути / test1.js

module.exports = function(app){

    app.get('/test1/', function(req, res){
        //...
    });

    //other routes..
}

Повторюючи це стільки разів, скільки мені потрібно, а потім, нарешті, у розміщенні app.js

require('./routes')(app);

1
мені більше подобається такий підхід, що дозволяє додавати нові маршрути без необхідності додавати щось основне в файл додатка.
Jason Miesionczek

3
Приємно, я також використовую цей підхід, з додатковою перевіркою розширення файлу, оскільки я стикався з проблемами з swp-файлами.
Geekfish

Вам також не потрібно використовувати readdirSync w / this, readdir прекрасно працює.
Павло

5
Чи є якісь накладні витрати на використання цього методу для читання файлів у каталозі порівняно з тим, що просто потрібні маршрути у вашому файлі app.js?
Абадаба

Я також хотів би знати те саме, що і @Abadaba. Коли це оцінюється, коли ви запускаєте сервер або кожен запит?
імнс

19

І будуючи ще більше на попередній відповіді, ця версія маршрутів / index.js буде ігнорувати будь-які файли, які не закінчуються у .js (і сам)

var fs = require('fs');

module.exports = function(app) {
    fs.readdirSync(__dirname).forEach(function(file) {
        if (file === "index.js" || file.substr(file.lastIndexOf('.') + 1) !== 'js')
            return;
        var name = file.substr(0, file.indexOf('.'));
        require('./' + name)(app);
    });
}

Дякую за це У мене хтось на Mac додав .DS_Storeфайли, і це все зіпсувало.
JayQuerie.com

19

Повна рекурсивна маршрутизація всіх .jsфайлів всередині /routesпапки, введіть це app.js.

// Initialize ALL routes including subfolders
var fs = require('fs');
var path = require('path');

function recursiveRoutes(folderName) {
    fs.readdirSync(folderName).forEach(function(file) {

        var fullName = path.join(folderName, file);
        var stat = fs.lstatSync(fullName);

        if (stat.isDirectory()) {
            recursiveRoutes(fullName);
        } else if (file.toLowerCase().indexOf('.js')) {
            require('./' + fullName)(app);
            console.log("require('" + fullName + "')");
        }
    });
}
recursiveRoutes('routes'); // Initialize it

в /routesвас ставити whatevername.jsі форматувати свої маршрути , як це:

module.exports = function(app) {
    app.get('/', function(req, res) {
        res.render('index', { title: 'index' });
    });

    app.get('/contactus', function(req, res) {
        res.render('contactus', { title: 'contactus' });
    });
}

8

Я намагаюся оновити цю відповідь на "express": "^ 4.16.3". Ця відповідь подібна до ShortRound1911.

server.js

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const db = require('./src/config/db');
const routes = require('./src/routes');
const port = 3001;

const app = new express();

//...use body-parser
app.use(bodyParser.urlencoded({ extended: true }));

//...fire connection
mongoose.connect(db.url, (err, database) => {
  if (err) return console.log(err);

  //...fire the routes
  app.use('/', routes);

  app.listen(port, () => {
    console.log('we are live on ' + port);
  });
});

/src/routes/index.js

const express = require('express');
const app = express();

const siswaRoute = require('./siswa_route');

app.get('/', (req, res) => {
  res.json({item: 'Welcome ini separated page...'});
})
.use('/siswa', siswaRoute);

module.exports = app;

/src/routes/siswa_route.js

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.json({item: 'Siswa page...'});
});

module.exports = app;

Я сподіваюся, що це може комусь допомогти. Щасливого кодування!


8

Якщо ви використовуєте express-4.x з TypeScript та ES6, це найкращий шаблон для використання:

src/api/login.ts

import express, { Router, Request, Response } from "express";

const router: Router = express.Router();
// POST /user/signin
router.post('/signin', async (req: Request, res: Response) => {
    try {
        res.send('OK');
    } catch (e) {
        res.status(500).send(e.toString());
    }
});

export default router;

src/app.ts

import express, { Request, Response } from "express";
import compression from "compression";  // compresses requests
import expressValidator from "express-validator";
import bodyParser from "body-parser";
import login from './api/login';

const app = express();

app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());

app.get('/public/hc', (req: Request, res: Response) => {
  res.send('OK');
});

app.use('/user', login);

app.listen(8080, () => {
    console.log("Press CTRL-C to stop\n");
});

Набагато чистіше, ніж використання varта module.exports.


5

Один підхід до всіх цих відповідей:

var routes = fs.readdirSync('routes')
      .filter(function(v){
         return (/.js$/).test(v);
      });

Просто скористайтеся регулярною виразкою для фільтрування через тестування кожного файлу в масиві. Він не є рекурсивним, але він відфільтрує папки, які не закінчуються .js


5

Я знаю, що це давнє питання, але я намагався розібратися у чомусь подібному для себе, і саме там я опинився, тому хотів вирішити подібну проблему, якщо у когось інших є ті самі проблеми, які я ' м маючи. Там є чудовий модуль вузла, який називається consign, який робить багато файлових систем, які ви бачите тут (тобто - відсутні речі readdirSync). Наприклад:

У мене є спокійний додаток API, який я намагаюся створити, і я хочу розмістити всі запити, які переходять до '/ api / *', щоб бути автентифікованими, і я хочу зберігати всі мої маршрути, що йдуть в api, у їхній власний каталог (давайте просто назвемо це "апі"). У головній частині програми:

app.use('/api', [authenticationMiddlewareFunction], require('./routes/api'));

Всередині каталогу маршрутів у мене є каталог під назвою "api" та файл під назвою api.js. У api.js у мене просто:

var express = require('express');
var router = express.Router();
var consign = require('consign');

// get all routes inside the api directory and attach them to the api router
// all of these routes should be behind authorization
consign({cwd: 'routes'})
  .include('api')
  .into(router);

module.exports = router;

Все працювало як очікувалося. Сподіваюся, що це комусь допоможе.


5

Якщо ви хочете, щоб окремий .js-файл краще організував ваші маршрути, просто створіть змінну у app.jsфайлі, що вказує на його розташування у файловій системі:

var wf = require(./routes/wf);

тоді,

app.get('/wf', wf.foo );

де .fooякась функція оголошена у вашому wf.jsфайлі. напр

// wf.js file 
exports.foo = function(req,res){

          console.log(` request object is ${req}, response object is ${res} `);

}

1
+1. Такий підхід показаний на офіційному прикладі тут: github.com/strongloop/express/tree/master/examples/…
Метт Браун

1
Чи працює це для спільного використання глобальних функцій та змінних у app.js? Або вам доведеться їх "передавати" wf.fooтощо, оскільки вони виходять за межі сфери, як і в інших представлених рішеннях? Я маю на увазі випадок, коли зазвичай ви мали б доступ до спільних змінних / функцій у wf.foo, якщо вони не були відокремлені від app.js.
Давид

так, це так, якщо ви оголосите функцію 'foo' в app.js, тоді app.get ('/ wf', foo); буде працювати
NiallJG


0

Це, можливо, найдивовижніший запит / відповідь щодо переповнення стека. Я люблю рішення Сем / Бред вище. Думав, я би наголосив на застосованій мені версії async:

function loadRoutes(folder){
    if (!folder){
        folder = __dirname + '/routes/';
    }

    fs.readdir(folder, function(err, files){
        var l = files.length;
        for (var i = 0; i < l; i++){
            var file = files[i];
            fs.stat(file, function(err, stat){
                if (stat && stat.isDirectory()){
                    loadRoutes(folder + '/' + file + '/');
                } else {
                    var dot = file.lastIndexOf('.');
                    if (file.substr(dot + 1) === 'js'){
                        var name = file.substr(0, dot);

                        // I'm also passing argv here (from optimist)
                        // so that I can easily enable debugging for all
                        // routes.
                        require(folder + name)(app, argv);
                    }
                }
            });
        }
    });
}

Структура мого каталогу трохи інша. Я зазвичай визначаю маршрути в app.js (у кореневій директорії проекту) за допомогою require-ing './routes'. Отже, я пропускаю чек, index.jsоскільки я хочу включити і цей.

РЕДАКТУВАННЯ: Ви також можете поставити це у функцію і викликати це рекурсивно (я редагував приклад, щоб це показати), якщо ви хочете вкласти свої маршрути в папки довільної глибини.


2
Чому ви хочете версію aysnc? Імовірно, ви хочете налаштувати всі свої маршрути перед тим, як почати обслуговувати трафік, інакше ви можете в кінцевому підсумку надіслати деякі "помилкові" 404.
Джо Абрамс

6
Справді. Я написав це, поки ще вивчаю вузол. Я погоджуюся в ретроспективі, що це не має сенсу.
tandrewnichols

0

ви можете розмістити всі маршрутні функції в інших файлах (модулях) і пов'язати їх з основним файлом сервера. в основний експрес-файл додайте функцію, яка з'єднає модуль із сервером:

   function link_routes(app, route_collection){
       route_collection['get'].forEach(route => app.get(route.path, route.func));
       route_collection['post'].forEach(route => app.post(route.path, route.func));
       route_collection['delete'].forEach(route => app.delete(route.path, route.func));
       route_collection['put'].forEach(route => app.put(route.path, route.func));
   }

і викликати цю функцію для кожної моделі маршруту:

link_routes(app, require('./login.js'))

у файлах модулів (наприклад - файл login.js) визначте функції як завжди:

const login_screen = (req, res) => {
    res.sendFile(`${__dirname}/pages/login.html`);
};

const forgot_password = (req, res) => {
    console.log('we will reset the password here')
}

та експортувати його за допомогою методу запиту як ключ, а значення - це масив об'єктів, кожен з яких має шлях та функціональні клавіші.

module.exports = {
   get: [{path:'/',func:login_screen}, {...} ],
   post: [{path:'/login:forgotPassword', func:forgot_password}]
};   
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.