如何在Express中的多个文件中包括路由处理程序?


223

在我的NodeJS express应用程序中,我有app.js一些常用的路由。然后在wf.js文件中,我想定义更多的路由。

如何app.js识别wf.js文件中定义的其他路由处理程序?

一个简单的要求似乎不起作用。


Answers:


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)提出了更好的建议:vimeo.com/56166857
avetisk 2013年

解决了多个文件的路由问题,但是app.js中定义的功能无法在路由中访问。
XIMRX

5
如果你需要一些功能只是把它们到另一个模块/文件并要求它从两个app.js和routes.js
BFil

2
我了解所有听到的声音,但require('./ routes')(app)这个语法让我震惊,有人可以告诉我这到底是什么,或者据我所知,它的传递对象“ app”的用途是什么
ANinJa

6
以下是这个问题的一个更好的答案– stackoverflow.com/a/37309212/297939
Dimitry

124

即使这个问题比较老,我还是偶然在这里寻找类似问题的解决方案。在尝试了这里的一些解决方案之后,我最终转向了另一个方向,并认为我会为最终在这里的其他任何人添加我的解决方案。

在express 4.x中,您可以获得路由器对象的实例,并导入另一个包含更多路由的文件。您甚至可以递归执行此操作,以便您的路由导入其他路由,从而使您可以创建易于维护的url路径。例如,如果我已经为'/ tests'端点设置了单独的路由文件,并且想为'/ tests / automated'添加新的路由集,则可能要将这些'/ automated'路由分解为另一个文件以保持我的“ / 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
这是最好的答案,应该放在榜首!谢谢
Kostanos

我可以将此结构用于Node Js Rest API吗?
MSM

@MSMurugan是的,您可以使用此模式构建rest api。
ShortRound1911

@ ShortRound1911我正在以此模式构建一个REST 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
我更好地喜欢这种方法,无需添加特定于主应用程序文件的任何内容即可添加新路由。
詹森·米森奇克

3
很好,我也使用这种方法,另外还检查了文件扩展名,因为我遇到了swp文件问题。
极客鱼2012年

您也不必使用带有此功能的readdirSync,readdir可以正常工作。
保罗

5
与只需要app.js文件中的路由相比,使用此方法读取目录中的文件有任何开销吗?
2012年

我也想和@Abadaba一样。什么时候进行评估,何时启动服务器或每个请求?
imns

19

并根据先前的答案建立更多的信息,此版本的route / 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 2012年

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.xTypeScript和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中使用的路由存储到自己的目录中(我们称之为“ 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等等,因为它们超出了其他提出的解决方案的范围?我指的是通常情况下,如果未将其与app.js分开,则将在wf.foo中访问共享变量/函数。
大卫

是的,如果您在app.js中声明了'foo'函数,那么app.get('/ wf',foo); 将起作用
NiallJG '16


0

这可能是有史以来最令人敬畏的堆栈溢出问题。我喜欢上面的 Sam / Brad的解决方案。以为我会加入我实现的异步版本:

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

我的目录结构有些不同。我通常通过require-ing 在app.js(在项目的根目录中)中定义路由'./routes'。因此,我跳过了针对的检查,index.js因为我也包括该检查。

编辑:如果您想将路由嵌套在任意深度的文件夹中,也可以将其放在函数中并递归调用(我编辑了示例以显示此内容)。


2
为什么要使用aysnc版本?大概您想在开始为流量提供服务之前设置所有路由,否则最终可能会发送一些“假” 404。
乔·艾布拉姆斯

6
确实。我在仍在学习节点的同时编写了它。我回想起来,这是没有道理的。
tandrewnichols

0

您可以将所有路由功能放在其他文件(模块)中,并将其链接到主服务器文件。在主express文件中,添加一个将模块链接到服务器的函数:

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

并以request方法作为键将其导出,值是一个对象数组,每个对象都有路径和功能键。

module.exports = {
   get: [{path:'/',func:login_screen}, {...} ],
   post: [{path:'/login:forgotPassword', func:forgot_password}]
};   
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.