midwayjs案例

276 阅读4分钟

对于做前端开发的同学来说,多少对服务端的知识有些了解,今天就带大家加深下nodejs服务端框架 Midwayjs一些介绍和开发案例。

官方文档:midwayjs.org/docs/intro

  1. Midway <- egg.js <- koa.js, midway 底层还是封装的koa.js


    MVC 架构优点****分离关注点:MVC 架构将数据处理、用户界面和控制流程分离,使得开发、测试和维护更为简单。代码复用:由于 MVC 架构的分离性,可以更容易地在不同的接口或平台上复用代码。并行开发:开发者可以在模型、视图和控制器上进行并行开发,提高了开发效率。灵活性:MVC 架构允许开发者更改视图层而不影响模型层,这为用户界面的更新和改进提供了便利。控制流程清晰:MVC 架构明确了控制流程,使得代码的阅读和理解更为容易


  2. 多编程范式

  3. 面向对象(OOP + Class + IoC):面向对象,类,容器

    // src/controller/index.tsimport { Controller, Get } from '@midwayjs/core';import { Context } from '@midwayjs/koa';
    @Controller('/')export class HomeController {
      @Inject()  ctx: Context
      @Get('/')  async home() {    return {      message: 'Hello Midwayjs!',      query: this.ctx.ip    }  }
    
  4. 函数式(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  }}
    
  5. @Controller() 装饰器

  6. 控制器文件一般来说在 src/controller 目录中,我们可以在其中创建控制器文件。Midway 使用 @Controller() 装饰器标注控制器,其中装饰器有一个可选参数,用于进行路由前缀(分组),这样这个控制器下面的所有路由都会带上这个前缀。

  7. 登录获取请求参数示例在路由层获取请求参数,如下

    import { Controller, Get, App, Post, Inject } from '@midwayjs/core';import { Application, Context } from '@midwayjs/koa';const url = require('url');const querystring = require('querystring');
    const todoList = [];@Controller('/formapi')export class HomeController {
      @Inject()  ctx: Context
      @App()  app: Application;
      // POST /api/todo  @Post('/todo')  async addTodo() {    let parsedUrl = url.parse(this.ctx.url);    let parsedQuery = querystring.parse(parsedUrl.query);
        todoList.push(parsedQuery);    return 'ok';  }
      // GET /api/todo  @Get('/todo')  async getTodo() {    return todoList;  }}
    
  8. Service 层,依赖注入

  9. 在业务中,只有控制器(Controller)的代码是不够的,一般来说会有一些业务逻辑被抽象到一个特定的逻辑单元中,我们一般称为服务(Service)。

  10. 比如我们在登录的时候做一些(复杂)操作,我们在service层做一些复杂的业务操作。

  11. 示例举例我们每一个用户添加uuid以及登录时间。

  12. 在servie层写业务逻辑,使用@provice装饰器进行抛出,这里options可以拿到 controller 层传过来的值

           import { Provide } from '@midwayjs/core';import { v4 as uuidv4 } from 'uuid';
           @Provide()export class LoginService {  async getUser(options: any) {    console.log(options, 'options');    // TODO: 登录逻辑,这里返回一个uuid    return {      uid: uuidv4(),      loginTime: new Date().getTime(),    };  }}
    

    修改controller层,使用 @Inject()装饰器进行注入,此时环境变量中就有loginService 变量可直接调用,service拿到值后进行处理后返回。

     ```
         @Inject()  loginService: LoginService;    @Post('/todo')  async addTodo() {    let parsedUrl = url.parse(this.ctx.url);    let parsedQuery = querystring.parse(parsedUrl.query);    let obj =await this.loginService.getUser(parsedQuery);    todoList = {...parsedQuery, ...obj};    return 'ok';   }
    ```
    

    通过访问接口返回值:

    图片

  13. 异常处理

  14. 普通异常定义在普通业务代码中自定义通用异常,一般很少用。

    // src/error/custom.error.ts// 自定义业务异常import { HttpStatus, MidwayHttpError } from '@midwayjs/core';
    export class CustomHttpError extends MidwayHttpError {  constructor() {    super('error', HttpStatus.BAD_REQUEST);  }}
    业务代码 console.log(options, 'options');    if (options?.username!== 'admin' || options?.password!== '123456') {      // throw new MidwayHttpError('my custom error', 23);      throw new CustomHttpError(); }
    
  15. 全局异常定义全局配置异常,比如404,503等, 使用@Catch 装饰器。

  16. // src/filter/internal.filter.ts

    import { Catch, httpError, MidwayHttpError } from '@midwayjs/core';import { Context } from '@midwayjs/koa';
    @Catch(httpError.NotFoundError)export class NotFoundError {  async catch(err: MidwayHttpError, ctx: Context) {    console.log(err);        // ...    return 'got 404 error, ' + err.message;  }}
    // configuration.tsimport { NotFoundError } from './filter/login.filter'  async onReady() {    // add middleware    this.app.useMiddleware([ReportMiddleware]);    // add filter    // this.app.useFilter([NotFoundFilter, DefaultErrorFilter]);    this.app.useFilter([NotFoundError]);
      }
    
  17. 链接数据库和Redis

    1. 安装依赖包

    npm i @midwayjs/typeorm@3 typeorm --save
    // mysql依赖npm install mysql --savenpm install mysql2 --save
    // redis缓存npm i @midwayjs/redis@3 --save
    
  18. 配置文件设置

    // configuration.ts
    import * as orm from '@midwayjs/typeorm';import * as redis from '@midwayjs/redis';
    @Configuration({  imports: [    // ...    orm,    redis                                                         // 加载 typeorm 组件  ],  importConfigs: [    join(__dirname, './config')  ]})
    

其中数据库需创建Model 实体类,使用@Entity() 

图片

// redis配置 
redis: {    client: {      port: xxxxx, // Redis port      host: "xxxxx", // Redis host      password: "xxxxx",      db: 0,    },  },
// 缓存配置直接调用@Inject()  redisService: RedisService;  @Get('/captcha')  async getCaptcha(){    let result = await this.redisService.get('ecp:platform:shop:access_token:11548297');    console.log(result, 'result');    // result =  Buffer.from( result || '', 'base64').toString()    return result;  }