Como incluir manipuladores de rota em vários arquivos no Express?

223

No meu expressaplicativo NodeJS , tenho app.jsalgumas rotas comuns. Então em umwf.js arquivo, eu gostaria de definir mais algumas rotas.

Como posso app.jsreconhecer outros manipuladores de rota definidos no wf.jsarquivo?

Um simples requisito não parece funcionar.

rafidude
fonte
2
verifique esta resposta stackoverflow.com/a/38718561/1153703 #
3100 Bikesh M

Respostas:

399

Se você deseja colocar as rotas em um arquivo separado , por exemplo routes.js, você pode criar o routes.jsarquivo desta maneira:

module.exports = function(app){

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

    //other routes..
}

E então você pode exigir que ele app.jspasse o app objeto desta maneira:

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

Veja também estes exemplos

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

BFil
fonte
18
Na verdade, o autor (TJ Holowaychuck) oferece uma abordagem melhor: vimeo.com/56166857
avetisk 23/03/13
Resolve o problema de roteamento de vários arquivos, mas as funções definidas no app.js não estão acessíveis nas rotas.
XIMRX
5
Se você precisar de algumas funções apenas colocá-los em outro módulo / arquivo e exigem-lo de ambos os app.js e routes.js
BFil
2
Eu entendi tudo ouvir, mas exigir ('./ routes') (app) esse síntex me surpreende, alguém pode me dizer o que é isso exatamente, ou qual é a utilidade disso, tanto quanto eu sei seu objeto de aplicativo de passagem "app"
ANinJa
6
Existe uma resposta melhor para esta pergunta abaixo - stackoverflow.com/a/37309212/297939 #
Dimitry
124

Embora essa seja uma pergunta mais antiga, eu tropecei aqui, procurando uma solução para um problema semelhante. Depois de tentar algumas das soluções aqui, acabei indo em uma direção diferente e pensei em adicionar minha solução para qualquer pessoa que acabasse aqui.

No express 4.x, você pode obter uma instância do objeto roteador e importar outro arquivo que contém mais rotas. Você pode até fazer isso recursivamente para que suas rotas importem outras, permitindo criar caminhos de URL fáceis de manter. Por exemplo, se eu já tiver um arquivo de rota separado para o terminal '/ tests' e desejar adicionar um novo conjunto de rotas para '/ tests / automatic', convém dividir essas rotas '/ automatic' em outro arquivo para mantenha meu arquivo '/ test' pequeno e fácil de gerenciar. Também permite agrupar rotas logicamente pelo caminho da URL, o que pode ser realmente conveniente.

Conteúdo de ./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);

Conteúdo de ./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;

Conteúdo de ./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
fonte
2
esta é a melhor resposta, deve estar no topo da lista! Obrigado
Kostanos
posso usar essa estrutura para a API Node Js Rest?
MSM
@MSMurugan sim, você pode criar uma API de descanso com esse padrão.
ShortRound1911
@ ShortRound1911 Estou construindo uma API de descanso desse padrão e colocado no servidor de hospedagem plesk, estou recebendo um erro
MSM
96

Com base no exemplo do @ShadowCloud, consegui incluir dinamicamente todas as rotas em um subdiretório.

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

Em seguida, coloque os arquivos de rota no diretório de rotas da seguinte maneira:

routes / test1.js

module.exports = function(app){

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

    //other routes..
}

Repetindo isso quantas vezes eu precisasse e finalmente no app.js colocando

require('./routes')(app);
Sam Corder
fonte
1
Eu gosto mais dessa abordagem, permite adicionar novas rotas sem precisar adicionar nada específico ao arquivo principal do aplicativo.
Jason Miesionczek
3
Bom, eu uso essa abordagem também, com uma verificação adicional da extensão do arquivo, pois tenho enfrentado problemas com os arquivos swp.
precisa saber é o seguinte
Você também não precisa usar o readdirSync com isso, o readdir funciona bem.
Paul
5
Existe alguma sobrecarga no uso desse método para ler os arquivos no diretório versus apenas exigir as rotas no seu arquivo app.js?
Abadaba 29/11
Também gostaria de saber o mesmo que @Abadaba. Quando isso é avaliado, quando você inicia o servidor ou a cada solicitação?
imns
19

E desenvolvendo ainda mais a resposta anterior, esta versão do routes / index.js ignorará todos os arquivos que não terminem em .js (e em si)

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
fonte
Obrigado por isso. Eu tinha alguém em um Mac adicionando .DS_Storearquivos e isso estava atrapalhando tudo.
JayQuerie.com
19

Roteamento recursivo completo de todos os .jsarquivos dentro da /routespasta, insira isso 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

em /routesvocê coloca whatevername.jse inicializa suas rotas assim:

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
fonte
8

Estou tentando atualizar esta resposta com "express": "^ 4.16.3". Esta resposta é semelhante a 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;

Espero que isso possa ajudar alguém. Feliz codificação!

Sukma Saputra
fonte
8

Se você estiver usando express-4.x com TypeScript e ES6, este seria o melhor modelo para usar:

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

Muito mais limpo do que usar vare module.exports.

gihanchanuka
fonte
5

Um ajuste para todas essas respostas:

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

Basta usar um regex para filtrar testando cada arquivo na matriz. Não é recursivo, mas filtrará as pastas que não terminam em .js

regretoverflow
fonte
5

Sei que essa é uma pergunta antiga, mas eu estava tentando descobrir algo parecido comigo e este é o lugar em que acabei, então queria colocar minha solução para um problema semelhante, caso outra pessoa tenha os mesmos problemas que eu ' estou tendo. Há um bom módulo de nó por aí chamado consign que faz muitas coisas do sistema de arquivos que são vistas aqui para você (ou seja, nada de readdirSync). Por exemplo:

Eu tenho um aplicativo API repousante que estou tentando criar e quero colocar todas as solicitações que vão para '/ api / *' para serem autenticadas e quero armazenar todas as minhas rotas que entram na api em seu próprio diretório (vamos chamá-lo de 'api'). Na parte principal do aplicativo:

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

Dentro do diretório de rotas, eu tenho um diretório chamado "api" e um arquivo chamado api.js. No api.js, eu simplesmente tenho:

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;

Tudo funcionou como esperado. Espero que isso ajude alguém.

Mike S.
fonte
5

Se você deseja que um arquivo .js separado organize melhor suas rotas, basta criar uma variável no app.jsarquivo apontando para sua localização no sistema de arquivos:

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

então,

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

Onde .fooestá alguma função declarada no seu wf.jsarquivo. por exemplo

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

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

}
NiallJG
fonte
1
+1. Esta é a abordagem mostrada no exemplo oficial aqui: github.com/strongloop/express/tree/master/examples/…
Matt Browne
1
Isso funciona para compartilhar funções e variáveis ​​globais no app.js? Ou você teria que "passá-los" para wf.fooetc., uma vez que estão fora do escopo, como nas outras soluções apresentadas? Estou me referindo ao caso em que você normalmente acessaria variáveis ​​/ funções compartilhadas no wf.foo se não fosse separado do app.js.
David David
sim, se você declarar a função 'foo' no app.js, app.get ('/ wf', foo); funcionará #
2389 NiallJG
0

Esta é possivelmente a (s) pergunta (s) mais impressionante (s) de estouro de pilha de todos os tempos. Eu amo as soluções de Sam / Brad acima. Pensei que iria concordar com a versão assíncrona que implementei:

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

Minha estrutura de diretórios é um pouco diferente. Normalmente, defino rotas no app.js (no diretório raiz do projeto) por require-ing './routes'. Conseqüentemente, estou pulando o cheque index.jsporque quero incluí-lo também.

EDIT: Você também pode colocar isso em uma função e chamá-lo recursivamente (editei o exemplo para mostrar isso) se quiser aninhar suas rotas em pastas de profundidade arbitrária.

tandrewnichols
fonte
2
Por que você quer uma versão aysnc? Presumivelmente, você deseja configurar todas as suas rotas antes de começar a servir o tráfego, ou então poderá enviar alguns 404 'falsos'.
Joe Abrams
6
De fato. Eu escrevi enquanto ainda aprendia o nó. Eu concordo em retrospecto que não faz sentido.
Tandrewnichols 03/12/2013
0

você pode colocar todas as funções de rota em outros arquivos (módulos) e vinculá-lo ao arquivo principal do servidor. no arquivo expresso principal, adicione uma função que vinculará o módulo ao servidor:

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

e chame essa função para cada modelo de rota:

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

nos arquivos do módulo (por exemplo - arquivo login.js), defina as funções normalmente:

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

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

e exporte-o com o método de solicitação como uma chave e o valor é uma matriz de objetos, cada um com teclas de caminho e função.

module.exports = {
   get: [{path:'/',func:login_screen}, {...} ],
   post: [{path:'/login:forgotPassword', func:forgot_password}]
};   
Tzvika Merchav
fonte