得益于 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. vue-router路由动态传参query和params的区别

    1.query方式传参和接收参数 //路由 { path: '/detail', //这里不需要参入参数 name: "detail", component: detail//这个 ...

  2. kmp算法:

    自学kmp算法: first time:wa #include<cstdio> #include<algorithm> #include<iostream> #in ...

  3. Redis的n种妙用,分布式锁,分布式唯一id,消息队列,抽奖……

    介绍 redis是键值对的数据库,常用的五种数据类型为字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset) Redis用作缓存,主要两个 ...

  4. 详解如何在 Linux 启动时自动执行命令或脚本

    我一直很好奇,在启动 Linux 系统并登录的过程中到底发生了什么事情.按下开机键或启动一个虚拟机,你就启动了一系列事件,之后会进入到一个功能完备的系统中,有时,这个过程不到一分钟.当你注销或者关机时 ...

  5. pyspark如何遍历broadcast

    因为论文关系要用到pyspark,具体情形如下: 有一个list=['aaa','bbb','ccc','ddd'],然后有一个rdd内数据类型是str,eg:'abcdefg',正常如果是需要筛选数 ...

  6. caffe编译错误记录

    1. caffe.pb.h丢失问题 错误代码如图: zhuoshi@zhuoshi-SYS-7048GR-TR:~/ZSZT/Geoffrey/caffe/caffe-master$ make all ...

  7. Metasploit远程调用Nessus出错

    Metasploit远程调用Nessus出错   从Nessus 7.1开始,Metaspliot远程调用Nessus创建新的扫描任务,会出现以下错误信息:   [*] New scan added ...

  8. Spring Cloud 微服务笔记(六)Spring Cloud Hystrix

    Spring Cloud Hystrix Hystrix是一个延迟和容错库,旨在隔离远程系统.服务和第三方库,阻止链接故障,在复杂的分布式系统中实现恢复能力. 一.快速入门 1)依赖: <dep ...

  9. centos7 安装 oh my zsh

    和在ubuntu 下安装十分相似(基本没区别) 安装zsh yum install zsh 改变系统bash chsh -s /bin/zsh git clone oh my zsh 项目: git@ ...

  10. 原生JS制作简易Tabs组件

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...