基础
- 文档:https://midwayjs.org/
- 默认选用 EggJS 作为我们的 Web 框架,也可以使用 Express 或者 Koa。
目录结构
- src
app.ts和agent.ts用于自定义启动时的初始化工作- app.ts : EggJS 扩展 Worker 生命周期文件(可选)
- agent.ts: EggJS 扩展 Agent 生命周期文件(可选)
- app文件夹
- EggJS 固定的根目录(可选 )
- config 文件夹
- 配置文件
Controller 控制器
- 在常见的 MVC 架构中,C 即代表控制器,控制器用于负责解析用户的输入,处理后返回相应的结果。
//路由前缀
@Controller('/')
@Controller()装饰器标注控制器,其中装饰器有一个可选参数,用于进行路由前缀(分组),这样这个控制器下面的所有路由都会带上这个前缀。
路由
控制器文件一般来说在 src/controller 目录中,我们可以在其中创建控制器文件。Midway 使用 @Controller() 装饰器标注控制器,其中装饰器有一个可选参数,用于进行路由前缀(分组),这样这个控制器下面的所有路由都会带上这个前缀。
service 服务
- 用来编写业务逻辑
使用服务
-
在controller控制器中来调用某个服务
-
只需在controller中利用依赖注入 装饰器 很方便的来调用服务
-
使用
@Provide装饰器暴露你的服务 -
使用
@Inject装饰器注入你的服务到属性上
-
理解IOC容器
ioc表示为控制反转
- 控制 :指的是对象创建(实例化、管理)的权力
- 反转 :控制权交给外部环境(IoC 容器)
-
@Provide() ,作用就是暴露IoC id ,能被 IoC容器 扫描到
- 暴露一个 class,让 IoC 容器能够获取元数据
-
@Inject,作用是注入一个IoC容器的对象
-
providerWrapper 函数作用来包裹一个函数,并且指定提供的 key,供其他 IoC 对象使用,否则其他ioc对象无法使用
依赖注入
@Provide 的作用是告诉 依赖注入容器,我需要被容器所加载。 @Inject 装饰器告诉容器,我需要将某个实例注入到属性上。
- 通过这两个装饰器的搭配,我们可以方便的在任意类中拿到实例对象
@Provide 和 @Inject 装饰器是有可选参数的,并且他们是成对出现。
Midway 的核心 “依赖注入” 容器会自动关联你的控制器(Controller) 和服务(Service),在运行过程中会自动初始化所有的代码,你无需手动初始化这些 Class
@Provide 装饰器的作用:
- 1、这个 Class,被依赖注入容器托管,会自动被实例化(new)
- 2、这个 Class,可以被其他在容器中的 Class 注入
而对应的 @Inject 装饰器,作用为:
- 1、在依赖注入容器中,找到对应的属性名,并赋值为对应的实例化对象
@Inject的类中,必须有@Provide才会生效。
默认情况下:
- 1、
@Provide取 类名的驼峰字符串 作为依赖注入标识符 - 2、
@Inject根据 规则 获取 key
规则如下:
- 1、如果装饰器包含参数,则以 参数字符串 作为 key
- 2、如果没有参数,标注的 TS 类型为 Class,则将类
@Provide的 key 作为 key - 3、如果没有参数,标注的 TS 类型为非 Class,则将 属性名 作为 key
getAsync
- MidwayContainer 有
getAsync方法用来异步处理对象的初始化 (很多依赖都是有异步初始化的需求)- 初始化sequelize
app.applicationContext用于全局作用域的 IoC 容器,所有的单例对象都存放于该属性,可以从中获取到单例对象。
await app.applicationContext.getAsync('xxx')
动态函数注入
在某些场景下,我们需要函数作为某个逻辑动态执行,而 IoC 中的对象属性则都是已经创建好的,无法满足动态的逻辑需求。
比如你需要一个工厂函数,根据不同的场景返回不同的实例,也可能有一个三方包,是个函数,在业务中想要直接调用,种种的场景下,你就需要直接注入一个函数,并且在函数中拿到上下文
-
midway 通过
providerWrapper函数来包裹一个函数,并且指定提供的 key,供其他 IoC 对象使用 -
通过
providerWrapper我们将一个原本的函数写法进行了包裹,和现有的依赖注入体系可以融合到一起,让容器能够统一管理 -
providerWrapper来暴露方法, 注意这个方法要被export导出,否则midway是无法识别这个工厂函数- 注意,动态方法必须 export,才会被依赖注入扫描到,默认为请求作用域(获取的 Container 是请求作用域容器)。
请求和响应 (Context)和应用(application)
所有的上层框架导出 请求和响应(Context)和 应用(Application)定义,我们都保持一致。即 Context 和 Application 。
生命周期
Midway 的生命周期是通过 src/configuration.ts 文件,实现 ILifeCycle 接口,就可以在应用 ready 的时候处理这类需求。
方法拦截器(切面)
- 拦截器一般会放在
src/aspect目录 - 在继承的情况下,拦截器不会对父类的方法生效。
切面很强大的功能,场景
- 校验、鉴权
比如,修改入参,我们只需要简单做个拦截,然后修改即可。
// src/controller/home.ts
export class HomeController {
@Get('/')
async home(data1, data2) {
return data1 + data2; // 因为拦截了方法,这里的返回值是 3
}
}
// src/aspect/
@Provide()
@Aspect(HomeController, 'home') // 这里只对 home 方法做拦截
export class ReportInfo implements IMethodAspect {
async before(point: JoinPoint) {
console.log(point.args); // 这里因为对 Controller 方法做切面,原本的参数为 [ctx, next]
point.args = [1, 2]; // 修改入参
}
}
参数校验和转换
文档:https://www.yuque.com/midwayjs/midway_v2/validate
方法调用时执行一些类型检查,参数转换的操作,Midway 提供了 @Validate 和 @Rule 装饰器,用来快速定义校验的规则,帮助用户减少这些重复的代码
- 最常用参数校验的地方是 控制器(Controller),同时你也可以在任意的 Class 中使用这个能力。
定义检查规则
利用@Rule装饰器
@Rule装饰器用于 修饰需要被校验的属性,它的参数为RuleType对象提供的校验规则的链式方法。
import { Rule, RuleType } from "@midwayjs/decorator";
export class UserDTO {
@Rule(RuleType.number().required())
id: number;
}
- 这里的
RuleType即为 joi 对象本身。- joi是校验库
开启检验能力
利用@Validate装饰器
- 开启校验能力还需要
@Validate装饰器
日志
@midwayjs/logger 包导出的方法,可以方便的接入不同场景的日志系统
- Midway 的日志系统基于社区的 winston
文件上传
- Egg.js 使用 egg-multipart 插件进行文件上传处理
egg框架内置了 Multipart 插件:
- 解析浏览器上传的
multipart/form-data的数据。 - 提供
file和stream两种处理接口模式供开发者选择。- file模式:适合简单的文件上传,前端传来的图片存到服务器
- stream模式:适合对文件进行进一步的处理,如压缩,解压等
- 默认提供了安全的限制。
获取到用户上传的数据后,开发者可以:
- 存储为本地文件。
- 提交给第三方服务,参见 通过 HttpClient 上传文件。
- 大部分情况下,我们会转存给云存储服务
上传/接受文件
前端
- 表单使用formData对象
- formData:表示表单数据的键值对key/value的形式提交到后台
后台
file模式:
-
单个文件 可使用ctx.request.files[0]获取
-
多个文件,
ctx.request.files属性进行遍历,然后分别进行处理
stream模式
ctx.getFileStream()接口能获取到上传的文件流。- 要通过
ctx.getFileStream便捷的获取到用户上传的文件,需要满足两个条件:- 只支持上传一个文件。
- 上传文件必须在所有其他的 fields 后面,否则在拿到文件流时可能还获取不到 fields。
定时任务
生成excel
extend 扩展
- 在src/extend文件夹扩展
- application.tx
- context.tx
- request.tx
- response.tx
- 增加扩展定于之后,需要在
src/interface.ts中扩展增加的 扩展的 属性
数据库
- 插件是 EggJS 的特色之一,Midway 也支持 EggJS 的插件体系。
npm i egg-mysql --save
src/config/plugin.js 中声明开启
import { EggPlugin } from 'egg';
export default {
static: false, // default is true
mysql: {
enable: true,
package: 'egg-mysql'
}
} as EggPlugin;
ORM
TypeORM
参考:https://www.yuque.com/midwayjs/midway_v2/orm
sequelize
中文: https://www.sequelize.com.cn/
官方: https://www.sequelize.com.cn/
sequelize-cli
- 迁移来跟踪对数据库的更改
- 现有数据库转移到另一个状态
- 这些状态转换保存在迁移文件中,这些文件描述如何进入新状态以及如何还原更改以返回旧状态
sequelize-typescript
reflect-metadata
- Reflect Metadata 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据