关于 Express 动态路由的问题

2014-07-17 00:40:25 +08:00
 serenader
楼主最近在用 Express 4.2.0 写一个项目,项目中有一个这样的需求:

用户可以自定义后台控制面板的地址。比如说,初始化的后台地址是 http://example.com/admin 。用户在后台中可以修改成 http://example.com/newadmin 。修改提交完成之后自动跳转到新的后台地址。访问原来的地址会提示无法访问。

我自己的思路是这样的,先在项目中新建一个 config.js 文件,把初始化数据存进去,初始化的后台地址为 /admin 。然后用户登录后台控制面板之后,可以提交新的后台地址,把这个新的后台地址存在数据库中。

在项目的路由控制中,我是这样写的:

var setting = require('../setting');
var config = require('../config');
setting.getSetting(function (err, s) {
//判断数据库中是否有 admin_path 这个值,如果有的话将 app.locals.adminPath 赋值为其值,否则
//赋值为 config 文件中的初始值。
app.locals.adminPath = (s && s.admin_path) ? s.admin_path : config.admin_path;
app.use(app.locals.adminPath, Router);
});


我这个思路从表面上看没什么问题,但是实际上行不通。实际的效果是,除非重新启动该项目,否则存储在数据库中的路径发挥不了作用。

我稍微分析了一下原因,发现是因为当运行该 Express 项目时,app.use(app.locals.adminPath, Router) 这个命令只会运行一次。而不是一次请求运行一次。所以就算修改 app.locals.adminPath 的值也没用了。

我试着在用户提交新的后台地址之后,把新的后台地址赋值给 app.locals.adminPath ,然后重新执行一次 app.use(app.locals.adminPath, Router) ,以为会覆盖之前的路径处理,但是结果不然。重新绑定路由已经没效果了。

现在我的方案是使用 supervisor 运行该项目,然后当用户成功存储后台地址到数据库的时候强制结束该项目进程。然后 supervisor 自动将其重启。这是一种比较笨的方法。

想问问各位大神,有没有更好的解决办法?
5044 次点击
所在节点    Node.js
21 条回复
paloalto
2014-07-17 01:31:36 +08:00
是个可供学习的好案例,看楼下有没有好的解决办法。
rankjie
2014-07-17 02:19:22 +08:00
简单点嘛,后面的 admin、newadmin 都拿来当URL参数处理不就好了

app.get "/:admin_path", (req, res)->

存个字典找下每个 admin_path 对应的用户,或者直接存数据库,从数据库里找不就可以了吗………

然后在 /:admin_path 下面的路径,加个中间件把对应的用户放到 res.admin_user 就可以了

比如

app.get "/:admin_path/test", fetchUser, (req ,res)->


不知道我对lz的意思理解的对不对
liteneo
2014-07-17 09:40:25 +08:00
我觉得简单的做法就是301跳转,永久重定向到新的admin_path,这样所有的/admin路由都可以统一处理,根据当前用户设置的admin_path然后永久重定向过去,二楼每次都需要轮询代价有点大吧。。。
serenader
2014-07-17 09:52:45 +08:00
@rankjie 感谢回复。是的,你没有理解错我的意思。

其实你的这个思路我之前也有想到过。在 StackOverflow 上面我也找到了类似这个问题的一个答案,其思路也是你这样的。

但是其实在这个地址后面我还有很多路由请求要处理,比如说

app.get('/admin/post')
app.post('/admin/post')
app.get('/admin/setting')
app.post('/admin/setting')

等等。如果采用你这种方法的话,那么,经过 /admin/ 这个路径的请求都要查询一次数据库,这对网站的性能有很大影响吧?

另外不知道你有没有注意到,其实我是用

app.use(app.locals.adminPath, Router)

这样的方法处理路由请求的。这样的话,程序运行的时候只会在刚开始运行的时候查询一次数据库。另外这样也比较方便,比如我想要处理 get /admin/post 的话,我只需在 Router 中绑定这样的处理:

Router.get('/post', function (req, res, next){});

这样在 Router 中就完全不用理会这个 /admin/ 这个路径了。

说了那么多,其实就是想表达,你说的这种方法会大量查询数据库,我个人觉得不是一个好办法。另外就是这种方法没办法用 app.use() 这个方法处理路由处理。


不过现在想想,其实重启一次程序也不是一件什么坏事。毕竟在生产环境中一般都会使用 supervisor 或者 forever 去让它永久运行的。所以如果说没有更好的办法的话,我也只能用最初的这种方法了。
serenader
2014-07-17 09:54:37 +08:00
@liteneo 能否详细说说过程?
jarlyyn
2014-07-17 09:58:15 +08:00
感觉应该前后台分离,然后后台修改后重启。
jarlyyn
2014-07-17 09:59:15 +08:00
@serenader
性能和数据库的问题,完全可以靠缓存解决吧?
serenader
2014-07-17 10:03:21 +08:00
@jarlyyn 前后台分离,什么意思呢?不太理解。

其实我是不想每次都查询一次数据库。缓存没使用过,所以没这个打算呢。
rekey
2014-07-17 10:05:03 +08:00
更改了后台地址以后重启下 server,动态生成相关的 router,这个应该不难吧?
jarlyyn
2014-07-17 10:05:44 +08:00
@serenader 作为两个进程来跑。
写web这种重数据查询的,不考虑缓存,个人无法理解。
个人认为,对于一般的web应用来说,最大的核心问题就是怎么做好缓存,或准备好以后怎么做缓存。
serenader
2014-07-17 10:07:12 +08:00
@rekey 现在我的方案就是更改之后就重启 server 。但是总觉得应该有个更优雅的方式,不用重启,达到这个目的。
rekey
2014-07-17 10:09:39 +08:00
@serenader 那你就绕不过缓存了。
serenader
2014-07-17 10:09:57 +08:00
@jarlyyn 谢谢回复。前后台用两个进程来跑 这个思路倒是没想过。可以试试。

不考虑缓存是因为现阶段还没有达到需要用到缓存的这个阶段。以后应该会使用到的。
rekey
2014-07-17 10:12:03 +08:00
@serenader app.locals.adminPath 你使用这个变量,就已经是缓存了。
serenader
2014-07-17 10:14:02 +08:00
@rekey 嗯。看来得花时间研究一下怎么做缓存了。
rekey
2014-07-17 10:17:22 +08:00
@serenader 简单来说,你就是不想放弃 express router 的便利性。又想能够动态的处理 url,其实二楼的方案真的写的很清楚了。

app.use() 其实已经可以直接处理掉一个 request 了。所以你认为 url 命中了以后可以直接调用相关的 router 处理。类似

app.use(function(req, res, next){
if(req.url){
require('./router/admin')(req,res,next);
}
});
serenader
2014-07-17 10:24:46 +08:00
@rekey 感谢回复!之前没想到可以用你说的这种方法。待会试试。谢谢。
gamexg
2014-07-17 11:12:50 +08:00
没用过 Express ,但是应该支持中间件。路由上固定为admin为管理地址。在中间件检测地址,如果是用户指定的管理地址则在中间件把地址修改为admin,之后路由还是一样处理。

不知道 Express 有url生成吗?有的话还得处理url生成部分。
rankjie
2014-07-17 12:10:34 +08:00
@liteneo
这么简单的kv结构要轮循?


@serenader
你要是觉得 数据库效率低、Redis等级的缓存系统还没必要,那你就存个json作缓存呗,第一次运行的时候把全部用户和他们的admin_path都读出来,然后以admin_path为key,用户信息为value,这样应该解决你的问题了
你想的这种方案居然要重启服务进程,这不是你阶段不阶段的问题,而是你整个架构都有问题,我觉得是有点邪门歪道,造了个三角形轮子
lijinma
2014-07-18 14:35:57 +08:00
@serenader

没看懂二楼 @rankjie 提供的解决方案有什么问题,我觉得能解决你的问题。

你回答说“ 如果采用你这种方法的话,那么,经过 /admin/ 这个路径的请求都要查询一次数据库,这对网站的性能有很大影响吧?”

能解释下,为什么这里是查数据库吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/123038

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX