首页 > 其他分享 >从0开发属于自己的nestjs框架的mini 版 —— koa-decorator路由篇

从0开发属于自己的nestjs框架的mini 版 —— koa-decorator路由篇

时间:2023-07-30 16:56:29浏览次数:41  
标签:function mini return string koa ctx export nestjs let

这篇主要是实现路由注解,用过nestjs的都知道,其路由都是通过注解来实现的,如有控制器@Controller(),@Get()...等等,nestjs 底层框架可选 是expres或者是Fastify,在这里我选择 koa2。

话不多说,直接上代码

src/koa-decorator.ts

引入相关库


import "reflect-metadata";
import path from "path";

类型声明

/******** 类型声明********* */
export type IMethondType = "get" | "post" | "delete" | "put" | "all";
export type IRouterType = {
  path: string | RegExp;
  methond: string | IMethondType;
};
//一个方法对应一个路由信息
export type IKeyMapRouters = {
  [methondName: string]: IRouterType;
};

// 类的元数据参数
export type IControllerMetate = {
  prefix: string | undefined;
  routers: IKeyMapRouters;
};


常量声明


/********常量声明********* */

export const CONTROLLER_META_KEY = Symbol("controller_meta_key"); // 控制器类装饰器key
export const MOTHOD_META_KEY = Symbol("method_meta_key"); // 类方法装饰器key
export const PARAMS_META_KEY = Symbol("params_meta_key"); // // 类方法参数装饰器key
export const DesignParamtypes = "design:paramtypes"; //内置的获取构造函数的参数

类控制装饰器


/*********类控制装饰器************** */

export function Controller(prefix?: string) {
  return function (target: Object) {
    let meta: IControllerMetate = getControllerMeta(target);
    meta.prefix = prefix;
    Reflect.defineMetadata(CONTROLLER_META_KEY, meta, target);
  };
}

// 获取类元数据参数
export function getControllerMeta(target: Object) {
  let classMeta: IControllerMetate = Reflect.getMetadata(
    CONTROLLER_META_KEY,
    target
  );
  if (!classMeta) {
    classMeta = { prefix: "", routers: {} };
    Reflect.defineMetadata(CONTROLLER_META_KEY, classMeta, target);
  }
  return classMeta;
}

类方法装饰器



/***************类方法装饰器************************ */

// 方法
export function RequestFactory(methond: string) {
  return function (path?: string) {
    return function (target: any, methodName: string, dec: PropertyDescriptor) {
      let classMeta: IControllerMetate = getControllerMeta(target);
      let methondMeta: IRouterType = { path: path || "", methond };

      classMeta.routers[methodName] = methondMeta;

      Reflect.defineMetadata(
        CONTROLLER_META_KEY,
        classMeta,
        target.constructor
      );
    };
  };
}

export const GET = RequestFactory("get");
export const POST = RequestFactory("post");
export const PUT = RequestFactory("put");
export const DELETE = RequestFactory("delete");
export const ALL = RequestFactory("all");

类方法参数装饰器


/**********类方法参数装饰器*********************** */

// 方法参数
export function factroyParameter(fn: Function) {
  return function (target: any, methodName: string, paramsIndex: number) {
    let meta: Array<Function> =
      Reflect.getMetadata(PARAMS_META_KEY, target, methodName) || [];
    meta.unshift(fn);
    Reflect.defineMetadata(PARAMS_META_KEY, meta, target, methodName);
  };
}

// 上下文装饰器
export function Ctx<T = any>() {
  return factroyParameter((ctx: T) => ctx);
}

//请求
export function Req<T extends { request: any }>() {
  return factroyParameter((ctx: T) => ctx.request);
}

// 响应
export function Res<T extends { response: any }>() {
  return factroyParameter((ctx: T) => ctx.response);
}
// url 的参数
export function Query<T extends { request: Record<any, any> }>(field?: any) {
  return factroyParameter((ctx: T) =>
    field ? ctx.request.query[field] : { ...ctx.request.query }
  );
}
// url 动态参数
export function Param<T extends { request: any }>() {
  return factroyParameter((ctx: T) => ctx.request.param);
}

//请求体参数
export function Body<T extends { request: any }>() {
  return factroyParameter((ctx: T) => ctx.request.body);
}
// 请求头
export function Header<T extends { request: Record<any, any> }>(field?: any) {
  return factroyParameter((ctx: T) =>
    field ? ctx.request.headers[field] : ctx.request.headers
  );
}

// next 方法
export function Next<T extends Function>() {
  return factroyParameter((_: any, next: T) => next);
}

注册类控制器的路由(核心方法)

/**
 * 注册类控制器的路由
 */
export function ResigerRouter(
  routerIntance: any,
  controllerInstance: Object | Function
) {
  if (!routerIntance) {
    throw `路由实例不能为空`;
  }
  if (typeof controllerInstance == "function") {
    controllerInstance = Reflect.construct(controllerInstance, []);
  }
  if (
    typeof controllerInstance == "object" &&
    typeof controllerInstance.constructor != "function"
  ) {
    throw `控制器不是一个类函数,注册失败`;
  }
  let { prefix, routers }: IControllerMetate = getControllerMeta(
    controllerInstance.constructor
  );
  for (let key in routers) {
    if (key == "constructor") return;

    let routerMeat = routers[key];
    let pathname;
    if (routerMeat.path instanceof RegExp) {
      pathname = routerMeat.path;
    } else if (typeof routerMeat.path !== "object") {
      pathname =
        path
          .join("/", prefix || "", routerMeat.path || "")
          .replace(/\\+/g, "/") || "/";
    }

    //方法参数
    let parameterMeta =
      Reflect.getMetadata(PARAMS_META_KEY, controllerInstance, key) || [];
    let actionFn = async (ctx: any, next: Function) => {
      let args = parameterMeta.map((item: Function) =>
        item(ctx, next, controllerInstance, key)
      );
      const resBody = await (controllerInstance as any)[key].call(
        controllerInstance,
        ...args
      );
      if (resBody !== undefined) ctx.body = await resBody;
      await next();
    };

    let methond = routerMeat.methond;
    routerIntance[methond](pathname, actionFn);
  }
}

测试用例

import koaRouter from "koa-router";
import Koa from "koa";
import { Controller, Ctx, GET, Query, ResigerRouter } from "./koa-decorator";

const app = new Koa();
let rotuer = new koaRouter({
  prefix: "/api",
});

@Controller("user")
class User {
  @GET("list")
  getUserId(@Query("id") id: string, @Ctx() ctx: Koa.DefaultContext): string {
    console.log("ctx", ctx);
    return this.computed(id);
  }

  computed(value: string): string {
    return value + "---computed";
  }
}
// 加载路由
ResigerRouter(rotuer, User);
app.use(rotuer.routes());
app.listen(8080, () => {
  console.log("server is run in prot 8080");
});


// 访问:http://127.0.0.1:8080/api/user/list?id=1

总结

1、这是使用koa ,koa-router 为底层,无破坏,无侵入实现的路由装饰器的框架
2、支持可以扩展更多的其他功能的装饰器
3、此路由框架与上以一篇的实现的ioc框架之前是没有关联的,目前两个都是单独无耦合的

标签:function,mini,return,string,koa,ctx,export,nestjs,let
From: https://www.cnblogs.com/beyonds/p/17591489.html

相关文章

  • MinimumBoundingBox2
    [ABC297F]MinimumBoundingBox2考虑解决一个稍简单的问题。给你一个\(n\timesm\)的矩形棋盘,要在上面放\(k\)个棋子,使得矩形\(4\)条边上都要有至少一个棋子。问方案数。样例输入:222样例输出:2总方案数\(C_{n\timesm}^k\)。发现不好做,我们于是试着求解\(4......
  • 从0开发属于自己的nestjs框架的mini 版- ioc篇
    如今,nodejs的框架也是层出不穷,偏向向底层的有express、koa、Fastify,偏向于上层有阿里的Egg、thinkjs、还有国外的nestjs。在这里我更喜欢nestjs,主要是其用了不同于其他框架的思想,采用分层,AOP(面向切面编程),OOP(面向对象编程)的设计思想。如果想要自己写一个类似的框架,该......
  • centos环境minio安装踩坑指南2023年7月30日
    MinIO的安装踩坑指南环境centos71.安装MinIO官方文档Binary下载,按照官网的路径配置比较快下载miniowgethttps://dl.min.io/server/minio/release/linux-amd64/minio修改minio的权限chmod+xminio移动下载的文件到此文件夹下sudomvminio/usr/local/bin/2.......
  • 【NestJS系列】核心概念:Controller控制器
    前言控制器主要是用来处理客户端传入的请求并向客户端返回响应。它一般是用来做路由导航的,内部路由机制控制哪个控制器接收哪些请求。路由为了创建基本控制器,我们需要使用@Controller装饰器,装饰器将类与所需元数据关联起来,并使Nest能够创建路由映射。我们使用nest-cli快速创建一个R......
  • 【NestJS系列】DI依赖注入与IOC控制反转
    前言上篇文章我们学习了如何使用nest-cli来快速生成一个NestJS后端项目,当我们打开编辑器查看代码时,会发现整个代码风格有点类似JAVA的spring框架,并且你会发现一些service类在controller控制器的constructor中注入后,可以不需要手动new就可以直接使用该类对应的实例方法。比如:import......
  • 【NestJS系列】核心概念:Providers提供者
    前言Providers是Nest中的一个基本概念,许多Nest中定义的类都可以被视为一个Provider,比如:service、repository、factory、helper等,它们都可以通过constructor注入依赖关系,这就意味着类与类之间可以创建各种依赖关系,并且维护各个类之间依赖关系的工作将委托给Nest运行时系统。Provider......
  • 【NestJS系列】从Nest CLI开始入门
    初识NestJSNest是一个渐进的Node.js框架,它可以在TypeScript和JavaScript(ES6、ES7、ES8)之上构建高效、可伸缩的企业级服务器端应用程序。Nest基于TypeScript编写并且结合了OOP(面向对象编程),FP(函数式编程)和FRP(函数式响应编程)的相关理念。在设计上的很多灵感来自于Ang......
  • CentOS 7 mini安装完配置
    背景CentOS8系统2021年12月31日已停止维护服务,CentOS7系统将于2024年06月30日停止维护服务。CentOS官方不再提供CentOS9及后续版本,不再支持新的软件和补丁更新。所以我们如果要安装服务器操作系统就需要安装centos7。这里我是用虚拟机安装的centos7mini版,mini版本是最小化安......
  • minio挂载到本地
    minioServer管理的文件存在于桶中,对于一部分需要直接访问文件系统的场景,需要将minioserver中的文件保存到本地直接读取文件。方法:contos7使用s3fs挂载minio对象存储实践https://blog.csdn.net/qq_43715659/article/details/127631590#创建s3fs密码文件#将ACCESS_KEY_ID替......
  • 使用ASP.NET Core Minimal API和MailKit发送电子邮件
    步骤1:创建新项目通过以下命令在终端中创建一个新的ASP.NETCoreWebAPI项目:dotnetnewwebapi-oSendingEmail由于我们正在使用MinimalAPIs,因此删除不必要的Controller文件夹和WeatherForecast类。步骤2:定义电子邮件数据传输对象(DTO)为了将数据从我们的API传递到邮件服务......