egg-router-group: 赋予eggjs路由群组操作的能力
发布于 2 年前 作者 zzzs 2819 次浏览 来自 分享

egg-router-group: 赋予eggjs路由群组操作的能力。

github: https://github.com/zzzs/egg-router-group

背景

最近在用egg做项目,发现当路由很多时,很多公共设置要每个去设置一遍,比如中间件等,于是想实现类似路由群组的功能,能为多个路由设置公共属性的功能。在这过程中,遇到了一些问题,进而也得到了一些提升,很高兴最终功能能实现,欢迎pr, star。

过程中学习到了一些内容:

  • egg-router, koa-router 的浅读
  • Proxy 的学习
  • es6 Reflect 的学习,能优雅的代替apply
  • 学单元测试时,学到的一些测试覆盖技巧

以下是具体描述

Features

为 app.router 注入了路由群组 group(options, cb) 方法, options: 路由群组公用属性, cb: 路由回调。

路由群组允许你共用路由属性,例如:路由别名前缀,路由url前缀,中间件等,你可以利用路由群组统一为多个路由设置共同属性,而不需在每个路由上都设置一次。共用属性被指定为对象格式,当作 app.router.group 方法的第一个参数,具体路由群组的相关内容,可参考下面几个常用样例来熟悉这些特性。

目前支持这三种属性

  • 路由别名前缀: name
  • 路由url前缀: prefix
  • 中间件: middlewares

Usage

基本用法

// {app_root}/app/router.js
module.exports = app => {
  const { router, controller, middleware } = app;
  const m1 = middleware.m1();
  const m2 = middleware.m2({ key: 'value' });p;
  const m3 = middleware.m3();

  router.group({ name: 'home::', prefix: '/pre', middlewares: [ m1, m2 ] }, router => {
    // router-path: /pre/test, middlewares: m1, m2
    router.get('/get', controller.home.get);
    // router-name: home::post, router-path: /pre/post, middlewares: m1, m2, m3
    router.post('post', '/post', m3, controller.home.post);
    
    // others
    router.all('/test', controller.home.all1);
    router.all('testname', '/test2', controller.home.all2);
    router.put('/put', controller.home.put);
    router.delete('/delete', controller.home.delete);
    router.del('/del', controller.home.del);
    router.options('/options', controller.home.options);
    router.patch('/patch', controller.home.patch);
    router.redirect('/redirect', '/');
    router.redirect('/redirect2', 'home::testname', 302);
  });

  // 设置单个属性
  router.group({ name: 'home::' }, router => {
    // router-path: /test
    router.get('/get', controller.home.get);
    // router-name: home::post, router-path: /post
    router.post('post', '/post', controller.home.post);
  });
};

中间件

支持传单个中间件或数组的形式

// {app_root}/app/router.js
module.exports = app => {
  const { router, controller, middleware } = app;
  const m1 = middleware.m1();
  const m2 = middleware.m2({ key: 'value' });

  router.group({ middlewares: [ m1, m2 ] }, router => {
    router.get('/test_m1', controller.home.test_m1);
    router.post('/test_m2/:id', controller.home.test_m2);
  });
  router.group({ middlewares: m1 }, router => {
    router.get('/test_m3', controller.home.test_m3);
    router.post('/test_m4/:id', controller.home.test_m4);
  });
};

路由别名前缀 & 路由url前缀

路由别名前缀 & 路由url前缀 只支持string类型

// {app_root}/app/router.js
module.exports = app => {
  // name
  router.group({ name: 'home::' }, router => {
    // router-name: home::post
    router.post('post', '/test/:id', controller.home.t1);
  });

  // prefix
  router.group({ prefix: '/pre' }, router => {
    // router-name: test, router-path: /pre/test2
    router.get('test', '/test2', controller.home.t2);
  });
};

Advanced Usage

路由子群组,顾名思义,即路由群组内也可以使用路由群组功能,很适用于大型项目,当路由到达一定量级时,也能轻松维护管理。如果你想的话,路由群组可以一直嵌套下去。

注:如果出现设置重复的中间件,该中间件也会被执行多次,这部分自由度还是会开放给用户,不做特殊处理。

  // {app_root}/app/router.js
  router.group({ name: 'home1::', prefix: '/pre1', middlewares: m1 }, router => {
    // router-name: home1::name_g1, router-path: /pre1/test_g1, middlewares: m1, m2
    router.get('name_g1', '/test_g1', m2, controller.group.g1);

    router.group({ prefix: '/pre2', middlewares: m2 }, router => {
      // router-path: /pre1/pre2/test_g3, middlewares: m1, m2, m2
      router.get('/test_g3', m2, controller.group.g1);

      // router-path: /pre1/pre2/test_g4/:id, middlewares: m1, m2
      router.post('/test_g4/:id', controller.group.g2);

      router.group({ name: 'home2::' }, router => {
        // router-name: home1::home2::name_g6, router-path: /pre1/pre2/test_g6/:id, middlewares: m1, m2
        router.post('name_g6', '/test_g6/:id', controller.group.g2);
        // ...
      });
    });
  });

结语

如有问题或有优化的点,欢迎提出。

12 回复

@atian25 嘻嘻,谢谢大神,望能加入github/egg :)

@zzzs 放在自己仓库挺好的,社区不一定要集中化的,那样更不容易维护。

可以考虑 https://github.com/eggjs-community 啥的,或者就放在自己那,然后加入 awsome-egg 那个 README 里面,更好

@atian25 受教了,明白😀

=.= 其实多提 pr 就行了,个人感觉加入一个开源组织并不是一件好差事,代表着责任啊,个人不喜欢太多压力。

@MiYogurt 类库加入的官方仓库的话,意味着,egg 的维护者都要承担起这个库的维护,同时这个库的作者也要承担起官方仓库其他类库的维护责任。

@MiYogurt 责任肯定要承担的,但也会获取一些关注,好的氛围也可以促进自我提升,相辅相成的。

eggjs-community 其实挺好的,可以在里面玩一些新奇的模块~

@okoala 是的,还能学到很多东西😃 From Noder

name 在 url 中怎么体现呢?加个访问的 url 例子会不会更方便理解一些

@hyj1991 嗯,这是路由别名前缀的意思,文章中还是有提到的,比如下面路由的别名则是 ‘home::post’, 路由别名相关也可访问egg#Router

router.group({ name: 'home::' }, router => {
    // router-name: home::post
    router.post('post', '/test/:id', controller.home.t1);
});

添加了个大杀器

Advanced Usage

路由子群组,顾名思义,即路由群组内也可以使用路由群组功能,很适用于大型项目,当路由到达一定量级时,也能轻松维护管理。如果你想的话,路由群组可以一直嵌套下去。

注:如果出现设置重复的中间件,该中间件也会被执行多次,这部分自由度还是会开放给用户,不做特殊处理。

  // {app_root}/app/router.js
  router.group({ name: 'home1::', prefix: '/pre1', middlewares: m1 }, router => {
    // router-name: home1::name_g1, router-path: /pre1/test_g1, middlewares: m1, m2
    router.get('name_g1', '/test_g1', m2, controller.group.g1);

    router.group({ prefix: '/pre2', middlewares: m2 }, router => {
      // router-path: /pre1/pre2/test_g3, middlewares: m1, m2, m2
      router.get('/test_g3', m2, controller.group.g1);

      // router-path: /pre1/pre2/test_g4/:id, middlewares: m1, m2
      router.post('/test_g4/:id', controller.group.g2);

      router.group({ name: 'home2::' }, router => {
        // router-name: home1::home2::name_g6, router-path: /pre1/pre2/test_g6/:id, middlewares: m1, m2
        router.post('name_g6', '/test_g6/:id', controller.group.g2);
        // ...
      });
    });
  });
回到顶部