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.jsonTypeScript 配置文件
  • 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 }
  }
}

参考资料