如何使用express.js配置动态路由

70

我有一个route.js文件,它看起来像这样:

module.exports = function(app) {

  app.get('/tip', function(req, res) {
    res.render("tip");
  });

  app.get('/article', function(req, res) {
   res.render("article");
  });

  app.get('/article1', function(req, res) {
   res.render("article1");
  });

  app.get('/article2', function(req, res) {
   res.render("article2");
  });

  app.get('/article3', function(req, res) {
   res.render("article3");
  });

  app.get('/modules/:name', function(req, res) {
    var name = req.params.name;
    res.render('modules/' + name);
  });

  app.get('/modules/esaver/:name', function(req, res) {
    var name = req.params.name;
    res.render('modules/esaver/' + name);
  });

};

考虑到我需要创建超过200个不同的路由,我最终会得到类似于“article1”、“article2”等的东西。

而我的app.js文件如下:

var express = require('express')
  ,http = require('http')
  ,fs = require('fs')
  ,path = require('path');

var app = express();

html_templates = __dirname + '/html_templates';

app.set('views', html_templates + '/views');
app.set('view engine', 'jade');

app.use('/Core', express.static(__dirname + '/Core'));


app.listen(3000, function () {
 console.log("express has started on port 3000");
});

require('./html_templates/controller/routes.js')(app);

有没有动态方式来创建这个?


现在看来,ExpressJS 对于动态路径支持通配符(*)的处理还不错:/one/*/two/three - vitaly-t
7个回答

86

我会为/modules/:name做和你一样的事情。

app.get('/article/:id', function(req , res){
  res.render('article' + req.params.id);
});

从休息的角度来看,这将更有意义。

如果由于特定原因而无法这样做,您可能想要尝试类似以下的操作:

var articlesEndpoints = ['/article2', '/article3'];
articlesEndpoints.forEach(function(name) {
  app.get(name, function(req, res) {
    res.render(name);
  });
});

这是你想表达的意思吗?


是的,我正在寻找类似的东西,但是我遇到了一个错误:错误:在视图目录“C:\ .....”中查找视图“/article”失败(我已经使用了foreach)。 - wasiim_dev
你确定 name 是你期望的吗?你可能想要记录它并查看一下。 - Alberto Zaccagni
1
@wasiim_dev 如果你的视图文件夹在 Express 代码所在的子目录 views 中,那么你需要在代码顶部添加 app.set('views', __dirname + '/views');。默认情况下,Express 会查找相对于当前工作目录的路径。 - mscdex
错误:在视图目录“C:\ inetpub \ wwwroot \ system \ Pulse \ src \ branches \ projectname / html_templates / views”中查找视图“/ article1”失败 在Function.app.render (C:\ inetpub \ wwwroot \ system \ Pulse \ src \ branches \ projectname \ node_modules \ express \ lib \ application.js: 506:17) - wasiim_dev
@wasiim_dev,为什么将文章作为模板?你应该将文章存储在数据库中,并拥有一个单独的“article”模板。使用文章ID(req.params.id)从数据库中获取文章并使用数据进行呈现res.render('article', articleData) - user3995789
显示剩余3条评论

29

终于搞定了..

在我得到 article1、article2 等文章的情况下:

app.get('/:name(article|article2|article3)?', function(req, res) {
    var name = req.params.name;
    res.render(name);
});

如果我遇到多层网址的情况,我会创建一个自定义函数:

function geturl(url) {

  app.get('/' + url + '/' + ':name', function(req, res){
    var name = req.params.name;
    res.render(url + '/' + name);
  });

};

1
我让它在路由结尾没有问号的情况下工作了,这有什么特别的意义吗? - Adam Fowler
@AdamF ? 使 req.params.name:name 变成可选项。例如,上述路径匹配空的 :name 的 '/' 和 '/article2',但不匹配 /abc - Mahsa2
你可能会发现这个在线 Express Route Tester 工具很有用。 - Mahsa2

9

有许多方法可以实现动态的express路由。这在很大程度上取决于您在项目中实施的结构,这里我留下一个动态路由的例子,希望它会对您有所帮助。

RouterService.js

module.exports = (function(myCustomRoutes) {
   let express = require('express');
   let router  = express.Router();
   let methods = Object.keys(myCustomRoutes); // getting methods ('get', 'post'... etc)
   let routesMethod = null;
   let url = null;

   for(i in methods) {
      routesMethod = Object.keys(myCustomRoutes[methods[i]]);
      for(j in routesMethod) {
         url = '/' + routesMethod[j];
         url += '/:' + myCustomRoutes[methods[i]][routesMethod[j]].params.join('/:');console.log(url);
         router[methods[i]](url, myCustomRoutes[methods[i]][routesMethod[j]].controller);
      }
   }

   return router;
})();

CustomRoutes.js

module.exports = (function() {
    let routes = {get: {}, post: {}};
    let routerService = require('./RouterService');

    // GET:  /dynamic1
    routes.get.dynamic1 = {
       params: [],
       controller: function(req, res, next) {
           res.send('route 1');
       }
    };

    // GET:  /dynamic2/:param1
    routes.get.dynamic2 = {
       params: [':param1'],
       controller: function(req, res, next) {
           res.send('route 2');
       }
    };
    // POST: /dynamic3/:param1/:param1
    routes.post.dynamic3 = {
       params: ['param1', 'param2'],
       controller: function(req, res, next) {
          res.send('route 3');
       }
    };

    /*
    *  Export a router with paths
    *  GET:  /dynamic1
    *  GET:  /dynamic2/:param1
    *  POST: /dynamic3/:param1/:param1
    **/
    return routerService(routes);
})();

app.js

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


/*
 *  Option 1
 *  GET:  /dynamic1
 *  GET:  /dynamic2/:param1
 *  POST: /dynamic3/:param1/:param1
 **/
 app.use(require('CustomRoutes')());


/*
 *  Option 2
 *  GET:  /api/v1/dynamic1
 *  GET:  /api/v1/dynamic2/:param1
 *  POST: /api/v1/dynamic3/:param1/:param1
 **/
 app.use('/api/v1', require('CustomRoutes')());

在routerservice函数中,对象实例化括号()不应该存在,否则在执行函数中的Object.keys时会导致NRE。 - abhijoseph

7

以下是我为实现动态API所做的事情,同时我可以控制哪个API允许访问哪些方法。从现在开始,要维护这些API,你只需要编辑APIs数组即可。

const APIs = [
    {
        route: 'order',
        methods: ['get', 'post']
    },
    {
        route: 'item',
        methods: ['get']
    },
]
APIs.forEach(api => {
    api.methods.forEach(method => {
        app[method]('/' + api.route, (req, res) => require('./routes/' + api.route)[method](req, res))
    })
})

2

以下是其他解决方案:

app.get(^\/article(\d{1,3})?\/?$, function(req, res, next) {
  var n;
  if (req.params[0])
    n = parseInt(req.params[0], 10);

  if (!n || (n > 0 && n < 900))
    res.render('article' + (n ? n : ''));
  else
    next();
});

要实现这个功能,可以使用app.all方法来解决,或者使用通用的中间件:

app.use(function(req, res, next) {
  var m = ^\/article(\d{1,3})?\/?$.exec(req.url);
  if (m) {
    var n;
    if (m[0])
      n = parseInt(m[0], 10);

    if (!n || (n > 0 && n < 900))
      return res.render('article' + (n ? n : ''));
  }
  next(); 
});

这可以部分解决问题。如果我有的不是文章而是(提示、食谱等等),这里有100多种可能性。我想要一个可以用作通用符号*后跟/项的东西。 - wasiim_dev

1
我创建了一个名为jadewalker的新模块。它会自动生成路由代码。
我们只需在你的jade或pug文件中添加一个名为jadewalker的注释即可。
//- jadewalker=/b,/b/:id
doctype html
html
 title b.jade
body
  p b.jade
  p params: #{params.id}

并将此模块添加到我们的app中。就这样。

var app = require('koa')()
var router = require('koa-router')();
router = require('jadewalker')(router, path.join(__dirname, 'views'));
app.use(router.routes());

我们可以通过URL http://localhost:3000/b/abc 访问我们的jade文件。 (^∀^)

-1

它在我的项目上工作正常

routesPath = path.join(__dirname, 'routes');

fs.readdirSync(routesPath).forEach(function(file) {
  require(routesPath + '/' + file)(app);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接