从0开发属于自己的nestjs框架的mini 版 —— 终极篇

发布时间 2023-07-30 17:42:57作者: 天高任鸟飞吧

这篇其实是最简单的,就是将前面所实现的ioc,路由整合在一起就可以了

话不多说,直接上代码

\src\koa-ioc.ts

引入相关库


import Koa from "koa";
import koaRouter from "koa-router";
import { ContainerModule } from "./ioc-module";
import { Type } from "./ioc-core.ts";
import { ResigerRouter } from "./koa-decorator";

声明类型


/****************声明类型************ */
/**
 * 异常响应类型
 */
export type IExceptionsFilter = (
  ctx: Koa.DefaultContext,
  error: Error
) => void | any | Promise<any>;

/**
 * 成功响应类型
 */
export type IResponseInterceptor = (ctx: Koa.DefaultContext) => unknown;

/**
 * 实例参数类型
 */
interface IKoaNestOption {
  koaOption?: ConstructorParameters<typeof Koa>; // ConstructorParameters获取构造函数参数的类型
  prefix?: string;
  routerOptions?: koaRouter.IRouterOptions;
  [key: string]: any;
}

NestFactory 实现的


export class NestFactory<T> {
  [x: string]: any;

  private iocInstance: ContainerModule<T>; // ioc容器实例

  private koaInstance: Koa; // koa 实例

  private routerInstance: koaRouter; // 路由实例

  private middleWareQuence: Array<Koa.Middleware> = []; // 全局中间件

  private exceptionsFilterQuence: Array<IExceptionsFilter> = []; // 全局异常拦截器(中间件)

  private responseInterceptorQuence: Array<IResponseInterceptor> = []; // 全局响应拦截器(中间件)

  constructor(appModule: Type<T>, options: IKoaNestOption = {}) {
    const { routerOptions = {}, koaOption = {} } = options || {};

    this.iocInstance = new ContainerModule(appModule);

    this.routerInstance = new koaRouter({
      ...routerOptions,
      prefix: options.prefix,
    });

    this.koaInstance = new Koa(koaOption);
  }

  private init() {
    this.appError();
    // 加载全局中间件
    this.koaInstance.use(this.setFirstMiddleware());
    this.middleWareQuence.forEach((middleware: Koa.Middleware) =>
      this.koaInstance.use(middleware)
    );
    // 加载路由
    this.loadRoutes();
  }

  // 设置第一个中间,可以捕获(任何一个中间件异常)全局响应和错误处理
  private setFirstMiddleware = () => {
    return async (ctx: Koa.DefaultContext, next: Function) => {
      try {
        ctx.requestId = Math.random();
        await next();
        this.responseInterceptorQuence.forEach((itme) => itme(ctx));
      } catch (error) {
        ctx.app.emit("error", error, ctx);
      }
    };
  };
  /**
   * 加载路由
   */
  private loadRoutes() {
    const ctrInstance = this.iocInstance.getControllerInstance();

    ctrInstance.forEach((itme) => ResigerRouter(this.routerInstance, itme));
    this.koaInstance.use(this.routerInstance.routes());
  }

  // 监听错误响应
  private appError() {
    this.koaInstance.on("error", (error: Error, ctx: Koa.DefaultContext) => {
      this.exceptionsFilterQuence.forEach((itme) => itme(ctx, error));
    });
  }

  //添加全局中间件
  public use(...middleware: Array<Koa.Middleware>) {
    this.middleWareQuence = [...this.middleWareQuence, ...middleware];
  }

  //添加全局异常拦截器
  public setGlobalExceptionsFilter(...fn: Array<IExceptionsFilter>) {
    this.exceptionsFilterQuence = [...this.exceptionsFilterQuence, ...fn];
  }
  // 添加全局响应拦截器
  public setGlobalResponseInterceptor(...fn: Array<IResponseInterceptor>) {
    this.responseInterceptorQuence = [...this.responseInterceptorQuence, ...fn];
  }

  //服务启动
  public listen(port: number, host?: string | Function, callback?: Function) {
    this.init();
    if (typeof host == "function") {
      callback = host;
      host = "0.0.0.0";
    }
    this.koaInstance.listen(port, host, callback && callback());
  }

  public getKoa() {
    return this.koaInstance;
  }
  public getRouter() {
    return this.routerInstance;
  }
}

测试用例


import Koa from "koa";
import { Controller, GET, Query, Ctx } from "./koa-decorator";
import { NestFactory } from "./koa-ioc";
import { Module } from "./ioc-module";
import { Injectable, Inject } from "./ioc-core";

@Injectable()
class UserService {
  private data: Array<any> = [];
  constructor() {
    this.data = [{ id: 1 }, { id: 2 }, { id: 3 }];
  }
  findUser(id: number) {
    return this.data.find((item) => item.id == id);
  }
}

@Controller("user")
class UserController {

  constructor(private userService: UserService) {}

  @GET("list")
  getUserId(@Query("id") id: number, @Ctx() ctx: Koa.DefaultContext) {
    console.log("ctx", ctx, id);
    let result = this.userService.findUser(id);
    if (!result) {
      throw new Error(`用户id [${id}] 不存在`);
    }
    return result;
  }
}

@Module({
  providers: [UserService],
  controllers: [UserController],
  imports: [],
})
class AppModule {}

function start() {
  const app = new NestFactory(AppModule, { prefix: "/api" });

  // 全局响应中间件
  app.setGlobalResponseInterceptor(async (ctx) => {
    ctx.body = await { data: ctx.body, success: true, status: 200 };
  });

  // 全局异常捕获中间件
  app.setGlobalExceptionsFilter(async (ctx, error) => {
    ctx.body = await {
      data: null,
      success: false,
      status: 500,
      message: error.message ? error.message : error,
    };
  });

  app.listen(8000, () => {
    console.info("app is runing in prot 8080");
  });
}
start();



总结

1、至此,一个mini 版本的nestjs 框架就完成了
2、重提一点,这个只是学习,对于使用nestjs 也是有很大帮助的
3、在此基础是可以扩展更多的功能,比如类中间件,方法中间件,守卫,管道等等也是可以的
4、这个是基础版本,想要更完善的功能学习的,可以参考 npm i @bylive/nestjs 这个增强版的