阅读本文前,需要提前阅读前置内容:
一、Midway 增删改查
二、Midway 增删改查的封装及工具类
三、Midway 接口安全认证
四、Midway 集成 Swagger 以及支持JWT bearer
五、Midway 中环境变量的使用
midway是阿里巴巴开源的,基于TypeScript语言开发的Nodejs后端框架。
本教程指导大家从0开始搭建一个midway项目。
其遵循遵循两种编程范式
- 面向对象(OOP + Class + IoC);
- 函数式(FP + Function + Hooks);
谁较容易上手学习
- 懂Nodejs技术的前端开发;
- 会TypeScript的后端开发;
在这里你可以掌握度如下知识
- 面向对象的开发体验;
- 增删改查及基类封装;
- 数据库操作;
- 缓存操作;
- 用户安全认证及访问安全控制;
- JWT访问凭证;
- 分布式访问状态管理;
- 密码加解密;
- 统一返回结果封装;
- 统一异常管理;
- Snowflake主键生成;
- Swagger集成及支持访问认证;
- 环境变量的使用;
- Docker镜像构建;
- Serverless发布;
本项目源码
https://github.com/bestaone/midway-boot
LIVE DEMO
http://midway-boot.hiauth.cn/swagger-ui/index.html
环境准备
- Nodejs 12+
- Npm 8+
- MySql 5+
- Redis
开发工具
我们这里使用 IntelliJ IDEA
下载地址:https://www.jetbrains.com/zh-cn/idea/download
安装数据库
略...
安装Redis
略...
第一个midway项目
初始化创建
>npm init midway
- 执行命令后,需要选择模板,标准项目需要选择:koa-v3;
- 项目名可以自定义(我这里设置为midway-boot);
启动
>cd midway-boot
>npm run dev
启动后浏览器访问:http://127.0.0.1:7001
调整ESLint配置
为了保证代码分隔统一,我们调整下ESLint配置
// .prettierrc.js
module.exports = {
...require('mwts/.prettierrc.json'),
endOfLine: "lf", // 换行符使用 lf
printWidth: 120, // 一行最多 120 字符
proseWrap: "preserve", // 使用默认的折行标准
semi: true, // 行尾需要有分号
}
在windows中代码的首行、尾行不能有空行,否则ESLint提示格式错误,可能是bug。
项目结构介绍
├─src # 源码目录
│ ├─config # 配置
│ ├─controller # 控制器
│ ├─entity # 数据对象模型
│ ├─filter # 过滤器
│ ├─middleware # 中间件
│ ├─service # 服务类
│ ├─configurations.ts # 服务生命周期管理及配置
│ └─interface.ts # 接口定义
├─test # 测试类目录
├─bootstrap.js # 启动入口
├─package.json # 包管理配置
├─tsconfig.json # TypeScript 编译配置文件
增删改查
ORM组件:TypeORM
TypeORM是Object Relation Mapping工具,提供的数据库操作能力。
安装依赖
>npm i @midwayjs/orm@3 typeorm --save
安装完后package.json
文件中会多出如下配置
{
"dependencies": {
"@midwayjs/orm": "^3.3.6",
"typeorm": "^0.3.7"
}
}
引入组件
在src/configuration.ts
中引入 orm 组件
// configuration.ts
import { Configuration, App } from '@midwayjs/decorator';
import * as koa from '@midwayjs/koa';
import * as validate from '@midwayjs/validate';
import * as info from '@midwayjs/info';
import { join } from 'path';
import { ReportMiddleware } from './middleware/report.middleware';
import * as orm from '@midwayjs/orm';
@Configuration({
imports: [
orm, // 引入orm组件
koa,
validate,
{
component: info,
enabledEnvironment: ['local'],
},
],
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
@App()
app: koa.Application;
async onReady() {
this.app.useMiddleware([ReportMiddleware]);
}
}
添加数据库配置
修改配置src/config/config.default.ts
// src/config/config.default.ts
import { MidwayConfig } from '@midwayjs/core';
export default {
keys: '1657707214114_9253',
koa: {
port: 7001,
},
// 添加orm配置
orm: {
type: 'mysql',
host: '127.0.0.1', // 改成你的mysql数据库IP
port: 3306, // 改成你的mysql数据库端口
username: 'root', // 改成你的mysql数据库用户名(需要有创建表结构权限)
password: '123456', // 改成你的mysql数据库密码
database: 'midway_boot',// 改成你的mysql数据库IP
synchronize: true, // 如果第一次使用,不存在表,有同步的需求可以写 true
logging: true,
},
} as MidwayConfig;
注意:首次启动没有创建表结构的,需要设置自动创建表接口
synchronize: true
安装MySql驱动
>npm install mysql2 --save
安装完后package.json
文件中会多出如下配置
{
"dependencies": {
"mysql2": "^2.3.3"
}
}
orm的详细文档见:http://www.midwayjs.org/docs/extensions/orm
Entity、Service、Controller
创建Entity实体类
- 创建目录
src/entity
; - 在该目录下创建实体类
user.ts
;
// src/entity/user.ts
import { EntityModel } from '@midwayjs/orm';
import {
Column,
CreateDateColumn,
PrimaryColumn,
UpdateDateColumn,
} from 'typeorm';
@EntityModel('user')
export class User {
@PrimaryColumn({ type: 'bigint' })
id: number;
@Column({ length: 100, nullable: true })
avatarUrl: string;
@Column({ length: 20, unique: true })
username: string;
@Column({ length: 200 })
password: string;
@Column({ length: 20 })
phoneNum: string;
@Column()
regtime: Date;
@Column({ type: 'bigint' })
updaterId: number;
@Column({ type: 'bigint' })
createrId: number;
@CreateDateColumn()
createTime: Date;
@UpdateDateColumn()
updateTime: Date;
@Column({ type: 'int', default: 1 })
status: number;
}
-
@EntityModel
用来定义一个实体类; -
@Column
用来描述类的一个熟悉,对应数据库就是一个数据列; -
@PrimaryColumn
用来定义一个主键,每个实体类必须要要主键; -
@PrimaryGeneratedColumn
用来定义一个自增主键; -
@CreateDateColumn
定义创建时,自动设置日期; -
@UpdateDateColumn
定义更新时,自动设置日期;
对应的数据库结构
CREATE TABLE `user` (
`id` bigint NOT NULL,
`avatarUrl` varchar(100) DEFAULT NULL,
`username` varchar(20) NOT NULL,
`password` varchar(200) NOT NULL,
`phoneNum` varchar(20) NOT NULL,
`regtime` datetime NOT NULL,
`updaterId` bigint NOT NULL,
`createrId` bigint NOT NULL,
`createTime` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updateTime` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
`status` int NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `IDX_78a916df40e02a9deb1c4b75ed` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
创建UserService
创建或者修改src/service/user.service.ts
文件。
// src/service/user.service.ts
import { Provide } from '@midwayjs/decorator';
import { User } from '../eneity/user';
import { InjectEntityModel } from '@midwayjs/orm';
import { Repository } from 'typeorm';
import { DeleteResult } from 'typeorm/query-builder/result/DeleteResult';
@Provide()
export class UserService {
@InjectEntityModel(User)
userModel: Repository;
async create(user: User): Promise {
return this.userModel.save(user);
}
async findById(id: number): Promise {
return this.userModel.findOneBy({ id });
}
async delete(id: number): Promise {
return this.userModel.delete(id);
}
}
-
@Provide
表示这个类将会由系统自动实例化,在使用的时候,只需要使用@Inject
注入就可以了; -
@InjectEntityModel
注入实体模型数据库操作工具;
注意:由于调整了UserService,
src/controller/api.controller.ts
、test/controller/api.test.ts
会报错,直接删掉即可
创建UserController
创建或者修改src/controller/user.controller.ts
文件。
// src/controller/user.controller.ts
import { Inject, Controller, Query, Post, Body } from '@midwayjs/decorator';
import { User } from '../eneity/user';
import { UserService } from '../service/user.service';
import { DeleteResult } from 'typeorm/query-builder/result/DeleteResult';
@Controller('/api/user')
export class UserController {
@Inject()
userService: UserService;
@Post('/create', { description: '创建' })
async create(@Body() user: User): Promise {
Object.assign(user, {
id: new Date().getTime(),
regtime: new Date(),
updaterId: 1,
createrId: 1,
});
return this.userService.save(user);
}
@Post('/findById', { description: '通过主键查找' })
async findById(@Query('id') id: number): Promise {
return this.userService.findById(id);
}
@Post('/delete', { description: '删除' })
async delete(@Query('id') id: number): Promise {
return this.userService.delete(id);
}
}
-
@Inject()
装饰类指定该对象会被自动注入;
单元测试
添加单元测试类
添加文件test/controller/user.test.ts
// test/controller/user.test.ts
import {close, createApp, createHttpRequest} from '@midwayjs/mock';
import {Application, Framework} from '@midwayjs/koa';
import {User} from '../../src/eneity/user'
describe('test/controller/user.test.ts', () => {
let app: Application;
let o: User;
beforeAll(async () => {
try {
app = await createApp();
} catch(err) {
console.error('test beforeAll error', err);
throw err;
}
});
afterAll(async () => {
await close(app);
});
// create
it('should POST /api/user/create', async () => {
o = new User();
Object.assign(o, {
username: new Date().getTime().toString(),
password: new Date().getTime().toString(),
phoneNum: new Date().getTime().toString(),
});
const result = await createHttpRequest(app).post('/api/user/create')
.send(o);
expect(result.status).toBe(200);
// 将创建好的数据存起来,以供后面测试使用(返回的数据会有id)
o = result.body;
});
// findById
it('should POST /api/user/findById', async () => {
const result = await createHttpRequest(app).post('/api/user/findById?id=' + o.id);
expect(result.status).toBe(200);
});
// delete
it('should POST /api/user/delete', async () => {
const result = await createHttpRequest(app).post('/api/user/delete?id=' + o.id);
expect(result.status).toBe(200);
});
});
-
beforeAll
、afterAll
分别会在测试开始前、后执行; -
createApp
BeforeAll阶段的error会忽略,需要手动处理异常;()
单元测试的详细文档,见:http://www.midwayjs.org/docs/testing
执行单元测试
>npm run test
如果测试时间过长,会导致测试失败,那么我们需要修改超时时间
修改测试类的超时时间
- 在根目录中添加文件
jest.setup.js
;
// jest.setup.js
// 只需要一行代码
// 设置单元测试超时时间
jest.setTimeout(60000);
- 修改
jest
配置文件jest.config.js
;
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testPathIgnorePatterns: ['/test/fixtures'],
coveragePathIgnorePatterns: ['/test/'],
// 添加如下一行代码,引入jest初始化文件
setupFilesAfterEnv: ['/jest.setup.js']
};
开发调试
IntelliJ IDEA中Debug
-
运行/调试配置
-
启动Debug
使用Postman测试
-
新增
-
查找
-
删除
版权所有,转载请注明出处 [码道功成]