【问题标题】:How to include route handlers in multiple files in Express? [duplicate]如何在 Express 的多个文件中包含路由处理程序? [复制]
【发布时间】:2011-08-28 21:22:42
【问题描述】:

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

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

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

【问题讨论】:

标签: node.js express


【解决方案1】:

如果你想把路由放在一个单独的文件中,例如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

【讨论】:

  • 其实作者(TJ Holowaychuck)给出了一个更好的方法:vimeo.com/56166857
  • 如果您需要一些功能,只需将它们放入另一个模块/文件中,并从 app.js 和 routes.js 中都需要它
  • 我听懂了一切,但 require('./routes')(app) 这个语法让我大吃一惊,谁能告诉我这到底是什么,或者这个有什么用处知道它传递的应用对象“app”
  • 下面这个问题有更好的答案——stackoverflow.com/a/37309212/297939
  • @RishabhAgrawal 它看起来比实际复杂。 require('./routes') 部分将导入在单独文件中创建的函数。 (app) 部分将以app 作为参数调用该函数。将函数导入为const routes = require('./routes'),然后调用为routes(app) 可能更容易理解。
【解决方案2】:

在 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 '/tests'
  .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;

【讨论】:

  • 这个是最好的答案,应该排在首位!谢谢
  • 我可以将这个结构用于 Node Js Rest API 吗?
  • @M.S.Murugan 是的,你可以用这种模式构建一个 rest api。
  • @ShortRound1911 我正在构建一个rest api这个模式并放到plesk托管服务器上,我收到一个错误
  • 这个答案应该是最好的
【解决方案3】:

以@ShadowCloud 的示例为基础,我能够在子目录中动态包含所有路由。

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

然后像这样将路由文件放在路由目录中:

routes/test1.js

module.exports = function(app){

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

    //other routes..
}

根据需要重复多次,最后在 app.js 中放置

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

【讨论】:

  • 我更喜欢这种方法,允许添加新路线而无需添加任何特定于主应用程序文件的内容。
  • 很好,我也使用这种方法,另外检查文件扩展名,因为我遇到了 swp 文件的问题。
  • 您也不必使用带有此功能的 readdirSync,readdir 工作正常。
  • 使用此方法读取目录中的文件与仅需要 app.js 文件中的路由相比是否有任何开销?
  • 我也想知道和@Abadaba一样。何时评估,何时启动服务器或在每个请求上?
【解决方案4】:

在上一个答案的基础上进一步构建,此版本的 routes/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 文件,结果搞砸了。
【解决方案5】:

完整递归路由/routes文件夹内的所有.js文件,把它放在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' });
    });
}

【讨论】:

  • 感谢您的精彩回答。帮了我很多。实施时,我意识到(1)您在需要时不需要'./',因为您使用的是路径。所以只需 require(fullName)(app); 和 (2) 你需要在开头添加这个 if (file === 'index.js') return false; 否则函数将无限期调用。
【解决方案6】:

如果您将 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");
});

比使用varmodule.exports 干净得多。

【讨论】:

  • 这是我在 2022 年最终使用的答案,谢谢!
【解决方案7】:

我正在尝试使用"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】:

    如果您想要一个单独的 .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。这是官方示例中显示的方法:github.com/strongloop/express/tree/master/examples/…
    • 这对于在 app.js 下共享全局函数和变量有用吗?或者您是否必须将它们“传递”到wf.foo 等,因为它们与其他提出的解决方案一样超出范围?我指的是通常你会访问 wf.foo 中的共享变量/函数的情况,如果它没有从 app.js 中分离出来。
    • 是的,如果你在 app.js 中声明了 'foo' 函数,那么 app.get('/wf', foo);会工作
    【解决方案9】:

    对所有这些答案进行一次调整:

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

    只需使用正则表达式通过测试数组中的每个文件进行过滤。不是递归的,但是会过滤掉不以.js结尾的文件夹

    【讨论】:

      【解决方案10】:

      我知道这是一个老问题,但我试图为自己找出类似的东西,这就是我最终找到的地方,所以我想把我的解决方案放在类似的问题上,以防其他人有同样的问题我遇到的问题。有一个很好的节点模块,叫做consign,它为你做了很多在这里看到的文件系统的东西(即 - 没有 readdirSync 的东西)。例如:

      我有一个正在尝试构建的 RESTful API 应用程序,我想将所有转到“/api/*”的请求都进行身份验证,并且我想将所有进入 api 的路由存储到他们自己的目录(让我们称之为'api')。在应用程序的主要部分:

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

      在 routes 目录中,我有一个名为“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;
      

      一切都按预期进行。希望这对某人有所帮助。

      【讨论】:

        【解决方案11】:

        我为此编写了一个小插件!厌倦了一遍又一遍地编写相同的代码。

        https://www.npmjs.com/package/js-file-req

        希望对你有帮助。

        【讨论】:

          【解决方案12】:

          index.js

          const express = require("express");
          const app = express();
          const http = require('http');
          const server = http.createServer(app).listen(3000);
          const router = (global.router = (express.Router()));
          app.use('/books', require('./routes/books'))
          app.use('/users', require('./routes/users'))
          app.use(router);
          

          路由/users.js

          const router = global.router
          router.get('/', (req, res) => {
              res.jsonp({name: 'John Smith'})
          }
          
          module.exports = router
          

          routes/books.js

          const router = global.router
          router.get('/', (req, res) => {
              res.jsonp({name: 'Dreams from My Father by Barack Obama'})
          }
          
          module.exports = router
          

          如果您的服务器在本地运行 (http://localhost:3000),那么

          // Users
          curl --request GET 'localhost:3000/users' => {name: 'John Smith'}
          
          // Books
          curl --request GET 'localhost:3000/books' => {name: 'Dreams from My Father by Barack Obama'}
          

          【讨论】:

            【解决方案13】:

            这可能是有史以来最棒的堆栈溢出问题/答案。我喜欢上面山姆/布拉德的解决方案。以为我会加入我实现的异步版本:

            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 './routes' 在 app.js(在项目的根目录中)定义路由。因此,我跳过了对 index.js 的检查,因为我也包括那个检查。

            编辑: 如果你想将你的路由嵌套在任意深度的文件夹中,你也可以把它放在一个函数中并递归调用它(我编辑了这个例子来展示这个)。

            【讨论】:

            • 您为什么想要 aysnc 版本?大概您想在开始提供流量之前设置所有路线,否则您最终可能会发送一些“错误”404。
            • 确实如此。我在学习节点时写了它。回想起来,我同意这没有意义。
            【解决方案14】:

            您可以将所有路由功能放在其他文件(模块)中,并将其链接到主服务器文件。 在主 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')
            }
            

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

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

            【讨论】:

              猜你喜欢
              • 2018-06-18
              • 2019-04-13
              • 1970-01-01
              • 2015-05-12
              • 2013-02-27
              • 2015-08-12
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多