得益于 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 详细文档

Egg 中 Controller 最佳实践的更多相关文章

  1. Spring Batch在大型企业中的最佳实践

    在大型企业中,由于业务复杂.数据量大.数据格式不同.数据交互格式繁杂,并非所有的操作都能通过交互界面进行处理.而有一些操作需要定期读取大批量的数据,然后进行一系列的后续处理.这样的过程就是" ...

  2. Require.js中的路径在IDEA中的最佳实践

    本文主要讲述require.js在IDEA中路径智能感知的办法和探索中遇到的问题. 测试使用的目录结构:一种典型的thinkphp 6的目录结构,如下图. 现在我通过在 vue-a.js 中运用不同的 ...

  3. C#异步编程中的最佳实践(做法)

    原文地址Stephen Cleary 写得很详细,尤其讲到了 GUI 上下文调用,在APS.NET中它会阻塞 GUI 线程,从而导致死锁.而控制台中却不存在这个问题. 比如开发过程中本地写控制台程序测 ...

  4. 【活动】美团技术沙龙第49期:AI在外卖场景中的最佳实践

    美团技术沙龙第49期开始啦! 本次沙龙,美团外卖技术部专家会深入介绍AI在对话系统.图像处理.个性化推荐.智能营销等方向在外卖业务中的实践,希望与业界技术同学一起交流学习. 无论你从事智能搜索,或是算 ...

  5. Git 在团队中的最佳实践--如何正确使用Git Flow

    我们已经从SVN 切换到Git很多年了,现在几乎所有的项目都在使用Github管理, 本篇文章讲一下为什么使用Git, 以及如何在团队中正确使用. Git的优点 Git的优点很多,但是这里只列出我认为 ...

  6. Android应用中MVP最佳实践

    转自:http://www.jianshu.com/p/ed2aa9546c2c 文/Jude95(简书作者)原文链接:http://www.jianshu.com/p/ed2aa9546c2c著作权 ...

  7. GIT在团队中的最佳实践

    我们已经从SVN 切换到Git很多年了,现在几乎所有的项目都在使用Github管理, 本篇文章讲一下为什么使用Git, 以及如何在团队中正确使用. Git的优点 Git的优点很多,但是这里只列出我认为 ...

  8. 一文搞懂 Python 的模块和包,在实战中的最佳实践

    最近公司有个项目,我需要写个小爬虫,将爬取到的数据进行统计分析.首先确定用 Python 写,其次不想用 Scrapy,因为要爬取的数据量和频率都不高,没必要上爬虫框架.于是,就自己搭了一个项目,通过 ...

  9. Git 在团队中的最佳实践--如何正确使用Git Flow[转]

    原文地址:http://www.cnblogs.com/cnblogsfans/p/5075073.html Git的优点 Git的优点很多,但是这里只列出我认为非常突出的几点. 由于是分布式,所有本 ...

随机推荐

  1. linux工具-journalctl查询日志

    有时候,当linux服务启动失败的时候,系统会提示我们使用journalctl -xe命令来查询详细信息,定位服务不能启动的原因. journalctl 用来查询 systemd-journald 服 ...

  2. el-table中单数行与双数行设置不同的背景颜色

    <el-table :cell-style='cellStyle' :data="tableData" style="width: 100%;" > ...

  3. linux extglob模式 和rm反选

    前言 extglob模式开启之后Shell可以另外识别出5个模式匹配操作符,能使文件匹配更加方便. 不然不识别! 正文 #开启命令: shopt -s extglob #关闭命令: shopt -u ...

  4. 末学者笔记--shell编程上 2 玄

    Shell编程-变量及表达运算 [内容简列] 1. shell变量简介 2. 定义变量 3. 使用变量 4. 修改变量的值 5. 单引号和双引号的区别 6. 将命令的结果赋值给变量 7. 删除变量 8 ...

  5. python的学习之路(二)

    1.字符串内置功能练习#!/usr/bin/env python# *_*coding:utf-8 *_*# Author: harsonname = 'harson'name =str('harso ...

  6. 解决Windows服务无法访问网络映射盘的问题

    下载工具psexec 下载地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/psexec 百度地址:https://pan.baid ...

  7. 关于vue的computed、filters、watch

    filters 这个属性大家可能用的不是很多 因为一般的数组过滤我们用 es6的filter就能完成了 我想到一个场景,网上买书促销 满100减50 满两百减100 <input type=&q ...

  8. Taro父子组件通信

    父组件 testEvent = () =>{ console.log('abc123') } <Test test={1231323} onTestEvent={this.testEven ...

  9. read()和write()

    1.write() 函数定义:ssize_t write (int fd, const void * buf, size_t count); 函数说明:write()会把参数buf所指的内存写入cou ...

  10. HDU - 1827 Summer Holiday (强连通)

    <题目链接> 题目大意: 听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间 ...