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

这篇具有很好参考价值的文章主要介绍了从0开发属于自己的nestjs框架的mini 版 —— koa-decorator路由篇。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这篇主要是实现路由注解,用过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框架之前是没有关联的,目前两个都是单独无耦合的文章来源地址https://www.toymoban.com/news/detail-615212.html

到了这里,关于从0开发属于自己的nestjs框架的mini 版 —— koa-decorator路由篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 扒开源安卓性能测试工具moblieperf源码——开发属于你自己的性能稳定性测试工具

    moblieperf由阿里巴巴开源的Android性能测试工具 下载:官方源码地址 mobileperf github 使用: 使用pycharm打开下载的项目 使用只需要修改配置文件 config.conf 即可 运行采集:a.mac、linux 在mobileperf工具根目录下执行sh run.sh ; b.windows 双击run.bat 配置图:(简单使用只需要修改包名和设

    2024年02月19日
    浏览(55)
  • Nestjs框架: 多环境参数配置

    多环境配置方案比较:dotenv vs config 1 )dotenv库 npmjs.com/package/dotenv 应用广泛,前后端, 解析.env文件,挂载到 process.env 属性上去 安装:$ npm i -S dotenv 按照官方示例配置 在.env文件上配置,对嵌套的数据不太友好,需要自行处理 2 )config库 npmjs.com/package/config nodejs环境读取配置文

    2024年02月22日
    浏览(36)
  • 如何自己制作一个属于自己的小程序?

    在这个数字化时代,小程序已经成为了我们生活中不可或缺的一部分。它们方便快捷,无需下载安装,扫一扫就能使用。如果你想拥有一个属于自己的小程序,不论是为了个人兴趣,还是商业用途,都可以通过编程或者使用免代码工具来实现。下面,我们就来探讨一下如何自

    2024年01月24日
    浏览(52)
  • nodejs框架 express koa介绍以及从零搭建 koa 模板

    操作 请求类型 url 返回 获取所有 GET /users 获取单个 GET /users/1 新增 POST /users 修改部分 PATCH /users/1 修改全部 PUT /users/1 删除 DELETE /users/1 一个路由由 请求方法 路径 和 回调函数组成 匹配 404 路由 获取请求报文参数 获取动态路由参数 获取 post 请求体参数 下载 响应 中间件本质是

    2024年02月14日
    浏览(35)
  • koa框架

    koa是由Express原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的Web框架。使用koa编写web应用,通过组合不同的generator,可以免除重复繁琐的 回调函数嵌套 ,并极大地提升错误处理的效率。koa不在内核方法中绑定任何中间件,它仅仅提供了-个轻量优雅的函数库,使

    2024年02月04日
    浏览(33)
  • 实现自己的mini-react

    选择Vanilla创建项目 选择javascript就行 删除多余文件 保留最简单目录 index.html main.js代码 这样就可以在浏览器上看到hello mini react了 首先抽离节点 渲染dom 可以看到结果是一样的 把上面的el和textNode封装一下方便调用 渲染dom 可以看到结果是一样的 创建一个DOM节点,根据el的类型

    2024年01月22日
    浏览(32)
  • 发布属于自己的 npm 包

    注意:输入密码的时候 不会显示出来,输入完整直接按回车即可

    2024年02月13日
    浏览(38)
  • node中间件-koa框架

    安装 npm i koa koa导出的是一个类,必须用 new 进行创建 koa也是通过注册中间件来完成请求操作的 koa注册的中间件提供了两个参数: ctx:上下文(Context)对象; koa并没有像express一样,将req和res分开,而是将它们作为ctx的属性; ctx代表一次请求的上下文对象; ctx.reque

    2024年02月16日
    浏览(53)
  • 免费搭建属于自己的域名个性邮箱

    当你已经拥有域名为 fzuenactus.org.cn的SSL证书时,你可以使用该证书来配置你的域名邮箱。以下是更详细的步骤: 1. 安装必要软件: 在终端中执行以下命令来更新系统和安装所需的软件包: 2. 配置Postfix: 编辑Postfix主配置文件: 确保以下配置正确设置: myhostname = mail.fzuenac

    2024年02月11日
    浏览(53)
  • 搭建一个属于自己的springboot项目

    最近公司要上个新系统,指定由我来带两个人进行开发,既然是新项目,那么项目搭建的事就落到我的头上了。现在都是使用springboot进行开发,为此我搭环境使用的是springboot,具体java环境如下, 使用springboot的版本是2.3.3.RELEASE。使用maven进行项目管理, 总结下,我使用到的

    2024年02月07日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包