Jak dołączyć procedury obsługi tras do wielu plików w Express?

223

W mojej expressaplikacji NodeJS mam app.jskilka wspólnych tras. Następnie w wf.jspliku chciałbym zdefiniować jeszcze kilka tras.

Jak mogę app.jsrozpoznać inne procedury obsługi tras zdefiniowane w wf.jspliku?

Proste wymaganie nie działa.

rafidude
źródło
2
sprawdź tę odpowiedź stackoverflow.com/a/38718561/1153703
Bikesh M

Odpowiedzi:

399

Jeśli chcesz na przykład umieścić trasy w osobnym pliku , routes.jsmożesz utworzyć routes.jsplik w ten sposób:

module.exports = function(app){

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

    //other routes..
}

A potem możesz wymagać od obiektuapp.js przekazania w ten sposób:app

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

Zobacz także te przykłady

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

BFil
źródło
18
W rzeczywistości autor (TJ Holowaychuck) podaje lepsze approche: vimeo.com/56166857
avetisk
Rozwiązuje problem routingu dla wielu plików, ale funkcje zdefiniowane w app.js nie są dostępne w trasach.
XIMRX
5
Jeśli potrzebujesz niektórych funkcji, po prostu umieść je w innym module / pliku i wymagaj od app.js i tras.js
BFil
2
Zrozumiałem, że wszystko słyszy, ale wymaga („./ tras”) (aplikacji) ten syntex zaskakuje, czy ktoś może mi powiedzieć, co to dokładnie jest, lub jaki jest pożytek z tego, o ile wiem, że jego obiekt aplikacji „app”
ANinJa
6
Poniżej znajduje się lepsza odpowiedź na to pytanie - stackoverflow.com/a/37309212/297939
Dimitry
124

Chociaż to starsze pytanie, natknąłem się tutaj, szukając rozwiązania podobnego problemu. Po wypróbowaniu niektórych rozwiązań tutaj postanowiłem pójść w innym kierunku i pomyślałem, że dodam moje rozwiązanie każdemu, kto tu trafi.

W express 4.x można uzyskać instancję obiektu routera i zaimportować inny plik zawierający więcej tras. Możesz to zrobić rekurencyjnie, aby Twoje trasy importowały inne trasy, umożliwiając tworzenie łatwych w utrzymaniu ścieżek URL. Na przykład, jeśli mam już osobny plik trasy dla mojego punktu końcowego „/ testy” i chcę dodać nowy zestaw tras dla „/ testów / automatycznych”, mogę rozdzielić te trasy „/ automatyczne” na inny plik, aby mój plik „/ test” powinien być mały i łatwy w zarządzaniu. Pozwala również logicznie grupować trasy według ścieżki URL, co może być naprawdę wygodne.

Zawartość pliku ./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);

Zawartość ./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;

Zawartość pliku ./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;
ShortRound1911
źródło
2
ta odpowiedź jest najlepsza, powinna znajdować się na szczycie listy! Dziękuję
Kostanos,
czy mogę użyć tej struktury dla Node Js Rest API?
MSM
@MSMurugan tak można zbudować API resztę z tym wzorem.
ShortRound1911
@ ShortRound1911 Jestem w stanie zbudować API resztę tego wzorca i umieścić na serwerze hostingowym plesk, pojawia się błąd
MSM
96

Opierając się na przykładzie @ShadowCloud, byłem w stanie dynamicznie uwzględnić wszystkie trasy w podkatalogu.

trasy / 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);
    });
}

Następnie umieszczaj pliki tras w katalogu tras w następujący sposób:

trasy / test1.js

module.exports = function(app){

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

    //other routes..
}

Powtarzam to tyle razy, ile potrzebowałem, a potem w końcu umieszczam app.js

require('./routes')(app);
Sam Corder
źródło
1
podoba mi się to podejście, pozwala dodawać nowe trasy bez konieczności dodawania niczego konkretnego do głównego pliku aplikacji.
Jason Miesionczek
3
Fajnie, używam również tego podejścia, z dodatkową kontrolą rozszerzenia pliku, ponieważ napotkałem problemy z plikami SWP.
Geekfish
Nie musisz także używać readdirSync w / this, readdir działa dobrze.
Paul
5
Czy jest jakieś obciążenie związane z używaniem tej metody do odczytu plików w katalogu, a nie tylko wymaganie tras w pliku app.js?
Abadaba
Chciałbym również wiedzieć to samo, co @Abadaba. Kiedy zostanie to ocenione, kiedy uruchomisz serwer lub na każde żądanie?
imns
19

I opierając się jeszcze bardziej na poprzedniej odpowiedzi, ta wersja trasy / index.js zignoruje wszystkie pliki nie kończące się na .js (i sama)

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);
    });
}
Brad Proctor
źródło
Dzięki za to. Miałem kogoś na komputerze Mac, który dodaje .DS_Storepliki i wszystko to psuje.
JayQuerie.com,
19

Umieść pełne rekurencyjne routing wszystkich .jsplików w /routesfolderze 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

w /routesumieścić whatevername.jsi zainicjować swoje trasy jak to:

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' });
    });
}
infinity1975
źródło
8

Próbuję zaktualizować tę odpowiedź za pomocą „express”: „^ 4.16.3”. Ta odpowiedź jest podobna do 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;

Mam nadzieję, że to może komuś pomóc. Miłego kodowania!

Sukma Saputra
źródło
8

Jeśli używasz express-4.x z TypeScript i ES6, byłby to najlepszy szablon do użycia:

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

Znacznie czystszy niż przy użyciu vari module.exports.

gihanchanuka
źródło
5

Jedna poprawka do wszystkich tych odpowiedzi:

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

Wystarczy użyć wyrażenia regularnego do filtrowania poprzez przetestowanie każdego pliku w tablicy. Nie jest rekurencyjny, ale odfiltruje foldery, które nie kończą się na .js

regretoverflow
źródło
5

Wiem, że to stare pytanie, ale próbowałem wymyślić coś dla siebie i to jest miejsce, w którym skończyłem, więc chciałem rozwiązać moje podobne problemy na wypadek, gdyby ktoś miał takie same problemy, jak ja ”. mam. Istnieje ładny moduł węzła o nazwie consign, który wykonuje wiele rzeczy z systemu plików, które są tu widoczne dla ciebie (tj. - nie ma readdirSync). Na przykład:

Mam spokojną aplikację API, którą próbuję zbudować i chcę umieścić wszystkie żądania kierowane do „/ api / *” w celu uwierzytelnienia i chcę zapisać wszystkie moje trasy, które idą w interfejsie API, do ich własnego katalogu (nazwijmy to „api”). W głównej części aplikacji:

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

Wewnątrz katalogu tras mam katalog o nazwie „api” i plik o nazwie api.js. W api.js po prostu mam:

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;

Wszystko działało zgodnie z oczekiwaniami. Mam nadzieję, że to komuś pomoże.

Mike S.
źródło
5

Jeśli chcesz, aby osobny plik .js lepiej organizował trasy, po prostu utwórz zmienną w app.jspliku wskazującą jego lokalizację w systemie plików:

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

następnie,

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

gdzie .foow twoim wf.jspliku zadeklarowana jest jakaś funkcja . na przykład

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

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

}
NiallJG
źródło
1
+1. Takie podejście pokazano w oficjalnym przykładzie tutaj: github.com/strongloop/express/tree/master/examples/…
Matt Browne
1
Czy działa to w przypadku udostępniania globalnych funkcji i zmiennych w app.js? A może musiałbyś je „przekazać” wf.fooitd., Ponieważ są one poza zakresem, tak jak w przypadku innych prezentowanych rozwiązań? Mam na myśli przypadek, w którym normalnie miałbyś dostęp do współużytkowanych zmiennych / funkcji w wf.foo, gdyby nie był oddzielony od app.js.
David
tak, jeśli zadeklarujesz funkcję „foo” w app.js, to app.get ('/ wf', foo); będzie działać
NiallJG
0

To prawdopodobnie najbardziej niesamowite pytanie / odpowiedź na pytanie o przepełnienie stosu. Ja kocham rozwiązań Sam / Brada powyżej. Pomyślałem, że włączy się z wersją asynchroniczną, którą zaimplementowałem:

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

Moja struktura katalogów jest trochę inna. Zazwyczaj definiuję trasy w app.js (w katalogu głównym projektu) przez require-ing './routes'. W związku z tym pomijam czek, index.jsponieważ ja chcę go również uwzględnić.

EDYCJA: Możesz również umieścić to w funkcji i wywołać rekurencyjnie (zredagowałem przykład, aby to pokazać), jeśli chcesz zagnieździć swoje trasy w folderach o dowolnej głębokości.

tandrewnichole
źródło
2
Dlaczego miałbyś chcieć wersji aysnc? Prawdopodobnie chcesz skonfigurować wszystkie swoje trasy, zanim zaczniesz obsługiwać ruch, albo może skończyć się wysyłaniem „fałszywych” 404.
Joe Abrams,
6
W rzeczy samej. Napisałem to podczas nauki węzła. Z perspektywy czasu zgadzam się, że to nie ma sensu.
tandrewnichols
0

możesz umieścić wszystkie funkcje trasy w innych plikach (modułach) i połączyć je z plikiem głównego serwera. w głównym pliku ekspresowym dodaj funkcję, która połączy moduł z serwerem:

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

i wywołaj tę funkcję dla każdego modelu trasy:

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

w plikach modułu (na przykład - plik login.js) zdefiniuj funkcje jak zwykle:

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

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

i eksportujemy go za pomocą metody żądania jako klucza, a wartością jest tablica obiektów, każdy ze ścieżką i klawiszami funkcyjnymi.

module.exports = {
   get: [{path:'/',func:login_screen}, {...} ],
   post: [{path:'/login:forgotPassword', func:forgot_password}]
};   
Tzvika Merchav
źródło