Midway 简介
Midway
基于TypeScript
开发,结合了面向对象(OOP + Class + IoC)
与函数式(FP + Function + Hooks)
两种编程范式,并在此之上支持了 Web / 全栈 / 微服务 / RPC / Socket / Serverless 等多种场景,致力于为用户提供简单、易用、可靠的Node.js
服务端研发体验
多编程范式
- 面向对象(OOP + Class + IoC)
// src/controller/home.ts
import { Controller, Get } from '@midwayjs/decorator'
import { Context } from '@midwayjs/koa'
@Controller('/')
export class HomeController {
@Inject()
ctx: Context
@Get('/')
async home() {
return {
message: 'Hello Midwayjs!',
query: this.ctx.ip,
}
}
}
- 函数式(FP + Function + Hooks)
// src/api/index.ts
import { useContext } from '@midwayjs/hooks'
import { Context } from '@midwayjs/koa'
export default async function home() {
const ctx = useContext<Context>()
return {
message: 'Hello Midwayjs!',
query: ctx.ip,
}
}
项目创建
npm init midway
# 选择模板
# koa-v3
项目结构
midway-demo
├── README.md
├── README.zh-CN.md
├── bootstrap.js
├── jest.config.js
├── package.json
├── src
│ ├── config
│ │ ├── config.default.ts
│ │ └── config.unittest.ts
│ ├── configuration.ts
│ ├── controller
│ │ ├── api.controller.ts
│ │ └── home.controller.ts
│ ├── filter
│ │ ├── default.filter.ts
│ │ └── notfound.filter.ts
│ ├── interface.ts
│ ├── middleware
│ │ └── report.middleware.ts
│ └── service
│ └── user.service.ts
├── test
│ └── controller
│ ├── api.test.ts
│ └── home.test.ts
└── tsconfig.json
项目目录说明
src
:源代码目录test
:测试代码目录config
:配置文件目录bootstrap.js
:启动文件jest.config.js
:测试配置文件tsconfig.json
:TypeScript
配置文件
src
目录controller
:控制器目录service
:服务目录middleware
:中间件目录filter
:过滤器目录interface.ts
:接口定义文件config
:配置文件目录
项目启动
yarn dev
Controller
- 查看
src/controller/home.controller.ts
文件
import { Controller, Get } from '@midwayjs/decorator'
@Controller('/')
export class HomeController {
@Get('/')
async home(): Promise<string> {
return 'Hello Midwayjs!'
}
}
路由装饰器
@Controller
:控制器装饰器,用于标记一个类为控制器- 增加参数
path
,用于指定控制器的路由前缀
// 访问 http://localhost:7001/api/home
@Controller('/api')
export class HomeController {}
Http 装饰器
@Get
:用于标记一个方法为GET
请求@Post
:用于标记一个方法为POST
请求@Put
:用于标记一个方法为PUT
请求@Delete
:用于标记一个方法为DELETE
请求@Patch
:用于标记一个方法为PATCH
请求@Head
:用于标记一个方法为HEAD
请求@Options
:用于标记一个方法为OPTIONS
请求@All
:用于标记一个方法为ALL
请求
import { Controller, Get, Post } from '@midwayjs/decorator'
@Controller('/test')
export class HomeController {
@Post('/')
async home(): Promise<string> {
return 'Hello Midwayjs!'
}
}
全局路由前缀
- 在
src/config/config.default.ts
文件中配置router
路由前缀
import { MidwayConfig } from '@midwayjs/core'
export default {
// use for cookie sign key, should change to your own and keep security
keys: '1653223786698_4903',
koa: {
port: 7001,
globalPrefix: '/demo',
},
} as MidwayConfig
依赖注入
Midway
通过@Inject
装饰器来实现依赖注入
// src/controller/home.controller.ts
import { Inject, Controller, Get } from '@midwayjs/decorator'
import { Context } from '@midwayjs/koa'
import { UserService } from '../service/user.service'
@Controller('/')
export class HomeController {
@Inject()
ctx: Context
@Inject()
userService: UserService
@Get('/get_user')
async getUser(@Query('uid') uid) {
const user = await this.userService.getUser({ uid })
return { success: true, message: 'OK', data: user }
}
}
// user.service.ts
// user.service.ts
import { Provide } from '@midwayjs/decorator'
import { IUserOptions } from '../interface'
@Provide()
export class UserService {
async getUser(options: IUserOptions) {
return {
uid: options.uid,
username: 'mockedName',
phone: '12345678901',
email: 'xxx.xxx@xxx.com',
}
}
}
@Provide
的作用是告诉 依赖注入容器 ,我需要被容器所加载。@Inject
装饰器告诉容器,我需要将某个实例注入到属性上。
连接 Mysql
- 安装
mysql
依赖
yarn add mysql mysql2 @midwayjs/orm typeorm
引入 TypeORM
- 在
src/configuration.ts
文件中引入TypeORM
// configuration.ts
import { Configuration } from '@midwayjs/decorator'
import * as orm from '@midwayjs/orm'
import { join } from 'path'
@Configuration({
imports: [
// ...
orm, // 加载 orm 组件
],
importConfigs: [join(__dirname, './config')],
})
export class ContainerConfiguratin {}
配置数据库连接
- 在
src/config/config.default.ts
文件中配置数据库连接
// config.default.ts
import { MidwayConfig } from '@midwayjs/core'
export default {
// use for cookie sign key, should change to your own and keep security
keys: '1653223786698_4903',
koa: {
port: 7001,
globalPrefix: '/demo',
},
orm: {
type: 'mysql',
host: '127.0.0.1',
port: 3306,
username: 'root',
password: '', // 数据库密码
database: 'midway', // 数据表
synchronize: true,
logging: false,
},
} as MidwayConfig
实现 model
- 在
src/model
目录下创建user.ts
文件
// user.ts
import { EntityModel } from '@midwayjs/orm'
import { Column, PrimaryGeneratedColumn } from 'typeorm'
// 映射user table
@EntityModel({ name: 'user' })
export class UserModel {
// 声明主键
@PrimaryGeneratedColumn('increment') id: number
// 映射userName和user表中的user_name对应
@Column({ name: 'user_name' }) userName: string
@Column({ name: 'age' }) age: number
@Column({ name: 'description' }) description: string
}
实现 service
- 在
src/service
目录下创建user.service.ts
文件
import { Provide } from '@midwayjs/decorator'
import { InjectEntityModel } from '@midwayjs/orm'
import { Repository } from 'typeorm'
import { IUserOptions } from '../interface'
import { UserModel } from '../model/user'
@Provide()
export class UserService {
// 注入实体
@InjectEntityModel(UserModel) userModel: Repository<UserModel>
async getUser(options: IUserOptions) {
return {
uid: options.uid,
username: 'mockedName',
phone: '12345678901',
email: 'xxx.xxx@xxx.com',
}
}
async addUser() {
let record = new UserModel()
record = this.userModel.merge(record, {
userName: 'migor',
age: 18,
description: 'test',
})
try {
const created = await this.userModel.save(record)
return created
} catch (e) {
console.log(e)
}
}
// 删除用户
async deleteUser() {
const record = await this.userModel
.createQueryBuilder()
.delete()
.where({ userName: 'migor' })
.execute()
const { affected } = record || {}
return affected > 0
}
// 更新用户信息
async updateUser() {
try {
const result = await this.userModel
.createQueryBuilder()
.update()
.set({
description: '测试更新',
})
.where({ userName: 'migor' })
.execute()
const { affected } = result || {}
return affected > 0
} catch (e) {
console.log('接口更新失败')
}
}
// 查询
async getUserList() {
const users = await this.userModel
.createQueryBuilder()
.where({ userName: 'migor' })
.getMany()
return users
}
}
实现 controller
- 在
src/controller
目录下创建user.controller.ts
文件
import { Inject, Controller, Get, Query } from '@midwayjs/decorator'
import { Context } from '@midwayjs/koa'
import { UserService } from '../service/user.service'
@Controller('/api')
export class APIController {
@Inject()
ctx: Context
@Inject()
userService: UserService
@Get('/get_user')
async getUser(@Query('uid') uid) {
const user = await this.userService.getUser({ uid })
return { success: true, message: 'OK', data: user }
}
@Get('/add_user')
async addUser() {
const user = await this.userService.addUser()
return { success: true, message: 'OK', data: user }
}
@Get('/get_user_list')
async getUsers() {
const user = await this.userService.getUserList()
return { success: true, message: 'OK', data: user }
}
@Get('/update_user')
async updateUser() {
const user = await this.userService.updateUser()
return { success: true, message: 'OK', data: user }
}
@Get('/delete_user')
async deleteUser() {
const user = await this.userService.deleteUser()
return { success: true, message: 'OK', data: user }
}
}
接入 Swagger
- 安装依赖
yarn add @midwayjs/swagger swagger-ui-dist
引入 Swagger
- 在
src/configuration.ts
文件中引入Swagger
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 * as orm from '@midwayjs/orm'
import * as swagger from '@midwayjs/swagger'
// import { DefaultErrorFilter } from './filter/default.filter';
// import { NotFoundFilter } from './filter/notfound.filter';
import { ReportMiddleware } from './middleware/report.middleware'
@Configuration({
imports: [
koa,
validate,
{
component: info,
enabledEnvironment: ['local'],
},
orm,
swagger,
],
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
@App()
app: koa.Application
async onReady() {
// add middleware
this.app.useMiddleware([ReportMiddleware])
// add filter
// this.app.useFilter([NotFoundFilter, DefaultErrorFilter]);
}
}
重启项目,访问
http://127.0.0.1:7001/swagger-ui/index.html
增加接口标签
- 在
src/controller/user.controller.ts
文件中增加接口标签
import { Inject, Controller, Get, Query } from '@midwayjs/decorator'
import { Context } from '@midwayjs/koa'
import { ApiOperation } from '@midwayjs/swagger'
import { UserService } from '../service/user.service'
@Controller('/api')
export class APIController {
@Inject()
ctx: Context
@Inject()
userService: UserService
@ApiOperation({ summary: '获取单个用户' })
@Get('/get_user')
async getUser(@Query('uid') uid) {
const user = await this.userService.getUser({ uid })
return { success: true, message: 'OK', data: user }
}
@ApiOperation({ summary: '增加单个用户' })
@Get('/add_user')
async addUser() {
const user = await this.userService.addUser()
return { success: true, message: 'OK', data: user }
}
@ApiOperation({ summary: '获取用户列表' })
@Get('/get_user_list')
async getUsers() {
const user = await this.userService.getUserList()
return { success: true, message: 'OK', data: user }
}
@ApiOperation({ summary: '更新单个用户' })
@Get('/update_user')
async updateUser() {
const user = await this.userService.updateUser()
return { success: true, message: 'OK', data: user }
}
@ApiOperation({ summary: '删除单个用户' })
@Get('/delete_user')
async deleteUser() {
const user = await this.userService.deleteUser()
return { success: true, message: 'OK', data: user }
}
}