从0开发属于自己的nestjs框架的mini 版 —— 终极篇
这篇其实是最简单的,就是将前面所实现的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 这个增强版的