Egg 中 Controller 最佳实践

得益于 JavaScript 加入的 decorator 特性,可以使我们跟 Java/C# 一样,更加直观自然的,做面向切面编程。而随着 TypeScript 的成熟,类型系统也让我们增强了信心,面对复杂的业务逻辑,也更有底气。

egg-controller 是集合了一些在 Controller 层开发中常见问题解决方案的插件。

Controller 路由定义

export class HomeController {
  @route('/api/xxx', { name: '获取XXX数据' })
  async getXXX(size: number, page: number) {
    return 'homeIndex';
  }
}

可以看到,使用 decorator 的形式来声明 Controller 非常直观,而且方便扩展,添加/修改 Controller 规则直接修改 decorator 的类型定义就好。这种形式也是 Java/C# 的常规操作。

这里的改进除了使用 decorator 替代了 router.js 来进行 Controller 声明以外,还添加了出入参支持,省去了需要手动读写 ctx 的过程,非常直观的声明 Controller 函数的参数需求,以及返回数据类型。

基于 decorator 的写法与之前最大的区别是,在 Controller 这个横切面,之前只有 loader 可以掌控,而现在可以在 decorator 中加以集中控制,再结合 TypeScript 的元信息,可以做出很多扩展,比如:

参数格式化

在 eggjs 中,因为没有类型信息的原因,从 params 和 query 中获取的信息都会是字符串类型,都需要在 Controller 中手动转换。而改造之后的写法,参数直接暴露在函数入参里,我们就可以直接拿写在入参的类型定义作为格式化的依据,根据类型尝试转换,保证参数类型正确,可以初步防止类型不符的参数进入到 Controller,省去手动判断、转换的逻辑。

参数校验

参数格式化只能保证参数的类型一致性,而我们的需求不止这些,比如必选参数为空时需要拦截,有时参数是复杂对象为了防止恶意构造数据,需要对数据格式做深度检测,所以这里引入了参数校验库,parameter,通过它来解决复杂的校验问题。

export class HomeController {
  @route('/api/xxx', { name: '获取XXX数据', validateMetaInfo: [{
    name: 'data',
    rule: {
      type: 'object',
      str: { type: 'string', max: 20 },
      count: { type: 'number', max: 10, required: false },
    },
   }] })
  async getXXX(data: { str: string, count?: number }>) {
    return data.str;
  }
}

这里有个问题,在类型是复杂类型时,TypeScript 默认生成的元数据里,获取不到类型的具体字段属性信息,而且前端直接引入复用后端的类型定义也比较麻烦。所以,想要在定义类型的同时,复用类型的定义,只能在编译时做工作,TypeScript 也开放出了编译时插件API,在不用编译时插件的情况下,就需要单独写一份规则的数据。

有插件后:

export class HomeController {
  @route('/api/xxx', { name: '获取XXX数据' })
  async getXXX(data: BaseValidateRule<{
    type: 'object',
    rule: {
      str: { type: 'string', max: 20 },
      count: { type: 'number', max: 10, required: false },
    },
  }>) {
    return data.str;
  }
}

路由级中间件

函数类型跟 egg 定义稍有不同:

(app: Application, typeInfo: RouteType) => (ctx: any, next: any) => any

egg 已经定义了中间件,为什么在路由上还定义一个?在路由定义的中间件跟全局的中间件区别在于范围,全局中间件更适合大规模的统一处理,用来统一处理特定业务功能接口就大材小用了,还需要设置过滤逻辑,甚至需要在 config 中设置黑白名单。而路由级中间件适合只有部分接口需要的统一处理,配合从 @route 上收集的类型信息处理更佳。

API文档 & 前端SDK

既然已经收集到了那么多元数据,根据这些数据生成API文档就很简单了无非就是前端的展示,也可以把数据转换对接其他的API文档平台。

更近一步,直接生成前端调用SDK?当然没问题。本插件支持通过模板生成,如果没有找到模板,会在SDK生成目录生成默认模板。

// Controller
export class MetaController {

  @route({ url: '/meta/index', name: '首页' })
  async index(id: string, n: number, e: 'enumA' | 'enumB', d: Date) {
    return 'metaIndex';
  }

}

// 生成代码
export class MetaService extends Base {

  /** 首页  */
  async index(id: string, n: number, e: string, d: Date) {
    const __data = { id, n, e, d };
    return await this.request({
      method: `get`,
      url: `/meta/index`,
      data: __data,
    });
  }

}

export const metaService = new MetaService();
export default new MetaService();

开发时

在配置中开启即可,根据需要自定义其他配置。当 Controller 中文件修改时,会同时重新生成对应的前端SDK文件。

构建打包时

在 前端打包流程前 可以使用 egg-controller gensdk 命令生成前端sdk,需要注意,如果为 TypeScript 项目,需要先将 TypeScript 编译,然后执行生成命令。

Controller 作为请求的起点,这只是个开始。

egg 框架

egg-controller 详细文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容