关于 Express 动态路由的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
serenader

关于 Express 动态路由的问题

  •  
  •   serenader 2014 年 7 月 17 日 5581 次点击
    这是一个创建于 4301 天前的主题,其中的信息可能已经有所发展或是发生改变。
    楼主最近在用 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 自动将其重启。这是一种比较笨的方法。

    想问问各位大神,有没有更好的解决办法?
    21 条回复    2014-07-18 14:59:10 +08:00
        1
    paloalto  
       2014 年 7 月 17 日
    是个可供学习的好案例,看楼下有没有好的解决办法。
    rankjie
        2
    rankjie  
       2014 年 7 月 17 日 via iPhone   1
    简单点嘛,后面的 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
        3
    liteneo  
       2014 年 7 月 17 日   1
    我觉得简单的做法就是301跳转,永久重定向到新的admin_path,这样所有的/admin路由都可以统一处理,根据当前用户设置的admin_path然后永久重定向过去,二楼每次都需要轮询代价有点大吧。。。
    serenader
        4
    serenader  
    OP
       2014 年 7 月 17 日
    @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
        5
    serenader  
    OP
       2014 年 7 月 17 日
    @liteneo 能否详细说说过程?
    jarlyyn
        6
    jarlyyn  
       2014 年 7 月 17 日   1
    感觉应该前后台分离,然后后台修改后重启。
    jarlyyn
        7
    jarlyyn  
       2014 年 7 月 17 日
    @serenader
    性能和数据库的问题,完全可以靠缓存解决吧?
    serenader
        8
    serenader  
    OP
       2014 年 7 月 17 日
    @jarlyyn 前后台分离,什么意思呢?不太理解。

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

    不考虑缓存是因为现阶段还没有达到需要用到缓存的这个阶段。以后应该会使用到的。
    rekey
        14
    rekey  
       2014 年 7 月 17 日   1
    @serenader app.locals.adminPath 你使用这个变量,就已经是缓存了。
    serenader
        15
    serenader  
    OP
       2014 年 7 月 17 日
    @rekey 嗯。看来得花时间研究一下怎么做缓存了。
    rekey
        16
    rekey  
       2014 年 7 月 17 日
    @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
        17
    serenader  
    OP
       2014 年 7 月 17 日
    @rekey 感谢回复!之前没想到可以用你说的这种方法。待会试试。谢谢。
    gamexg
        18
    gamexg  
       2014 年 7 月 17 日
    没用过 Express ,但是应该支持中间件。路由上固定为admin为管理地址。在中间件检测地址,如果是用户指定的管理地址则在中间件把地址修改为admin,之后路由还是一样处理。

    不知道 Express 有url生成吗?有的话还得处理url生成部分。
    rankjie
        19
    rankjie  
       2014 年 7 月 17 日 via iPhone
    @liteneo
    这么简单的kv结构要轮循?


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

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

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

    能解释下,为什么这里是查数据库吗?
    favormm
        21
    favormm  
       2014 年 7 月 18 日
    我也不明白,有人详细介绍一下吗?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2885 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 523ms UTC 03:13 PVG 11:13 LAX 20:13 JFK 23:13
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86