找回密码
 立即注册
首页 业界区 业界 从0开发属于自己的nestjs框架的mini 版 —— koa-decora ...

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

狙兕 2025-6-8 22:24:22
这篇主要是实现路由注解,用过nestjs的都知道,其路由都是通过注解来实现的,如有控制器@Controller(),@Get()...等等,nestjs 底层框架可选 是expres或者是Fastify,在这里我选择 koa2。
话不多说,直接上代码
src/koa-decorator.ts
引入相关库
  1. import "reflect-metadata";
  2. import path from "path";
复制代码
类型声明
  1. /******** 类型声明********* */
  2. export type IMethondType = "get" | "post" | "delete" | "put" | "all";
  3. export type IRouterType = {
  4.   path: string | RegExp;
  5.   methond: string | IMethondType;
  6. };
  7. //一个方法对应一个路由信息
  8. export type IKeyMapRouters = {
  9.   [methondName: string]: IRouterType;
  10. };
  11. // 类的元数据参数
  12. export type IControllerMetate = {
  13.   prefix: string | undefined;
  14.   routers: IKeyMapRouters;
  15. };
复制代码
常量声明
  1. /********常量声明********* */
  2. export const CONTROLLER_META_KEY = Symbol("controller_meta_key"); // 控制器类装饰器key
  3. export const MOTHOD_META_KEY = Symbol("method_meta_key"); // 类方法装饰器key
  4. export const PARAMS_META_KEY = Symbol("params_meta_key"); // // 类方法参数装饰器key
  5. export const DesignParamtypes = "design:paramtypes"; //内置的获取构造函数的参数
复制代码
类控制装饰器
  1. /*********类控制装饰器************** */
  2. export function Controller(prefix?: string) {
  3.   return function (target: Object) {
  4.     let meta: IControllerMetate = getControllerMeta(target);
  5.     meta.prefix = prefix;
  6.     Reflect.defineMetadata(CONTROLLER_META_KEY, meta, target);
  7.   };
  8. }
  9. // 获取类元数据参数
  10. export function getControllerMeta(target: Object) {
  11.   let classMeta: IControllerMetate = Reflect.getMetadata(
  12.     CONTROLLER_META_KEY,
  13.     target
  14.   );
  15.   if (!classMeta) {
  16.     classMeta = { prefix: "", routers: {} };
  17.     Reflect.defineMetadata(CONTROLLER_META_KEY, classMeta, target);
  18.   }
  19.   return classMeta;
  20. }
复制代码
类方法装饰器
  1. /***************类方法装饰器************************ */
  2. // 方法
  3. export function RequestFactory(methond: string) {
  4.   return function (path?: string) {
  5.     return function (target: any, methodName: string, dec: PropertyDescriptor) {
  6.       let classMeta: IControllerMetate = getControllerMeta(target);
  7.       let methondMeta: IRouterType = { path: path || "", methond };
  8.       classMeta.routers[methodName] = methondMeta;
  9.       Reflect.defineMetadata(
  10.         CONTROLLER_META_KEY,
  11.         classMeta,
  12.         target.constructor
  13.       );
  14.     };
  15.   };
  16. }
  17. export const GET = RequestFactory("get");
  18. export const POST = RequestFactory("post");
  19. export const PUT = RequestFactory("put");
  20. export const DELETE = RequestFactory("delete");
  21. export const ALL = RequestFactory("all");
复制代码
类方法参数装饰器
  1. /**********类方法参数装饰器*********************** */
  2. // 方法参数
  3. export function factroyParameter(fn: Function) {
  4.   return function (target: any, methodName: string, paramsIndex: number) {
  5.     let meta: Array<Function> =
  6.       Reflect.getMetadata(PARAMS_META_KEY, target, methodName) || [];
  7.     meta.unshift(fn);
  8.     Reflect.defineMetadata(PARAMS_META_KEY, meta, target, methodName);
  9.   };
  10. }
  11. // 上下文装饰器
  12. export function Ctx<T = any>() {
  13.   return factroyParameter((ctx: T) => ctx);
  14. }
  15. //请求
  16. export function Req<T extends { request: any }>() {
  17.   return factroyParameter((ctx: T) => ctx.request);
  18. }
  19. // 响应
  20. export function Res<T extends { response: any }>() {
  21.   return factroyParameter((ctx: T) => ctx.response);
  22. }
  23. // url 的参数
  24. export function Query<T extends { request: Record }>(field?: any) {
  25.   return factroyParameter((ctx: T) =>
  26.     field ? ctx.request.query[field] : { ...ctx.request.query }
  27.   );
  28. }
  29. // url 动态参数
  30. export function Param<T extends { request: any }>() {
  31.   return factroyParameter((ctx: T) => ctx.request.param);
  32. }
  33. //请求体参数
  34. export function Body<T extends { request: any }>() {
  35.   return factroyParameter((ctx: T) => ctx.request.body);
  36. }
  37. // 请求头
  38. export function Header<T extends { request: Record }>(field?: any) {
  39.   return factroyParameter((ctx: T) =>
  40.     field ? ctx.request.headers[field] : ctx.request.headers
  41.   );
  42. }
  43. // next 方法
  44. export function Next<T extends Function>() {
  45.   return factroyParameter((_: any, next: T) => next);
  46. }
复制代码
注册类控制器的路由(核心方法)
  1. /**
  2. * 注册类控制器的路由
  3. */
  4. export function ResigerRouter(
  5.   routerIntance: any,
  6.   controllerInstance: Object | Function
  7. ) {
  8.   if (!routerIntance) {
  9.     throw `路由实例不能为空`;
  10.   }
  11.   if (typeof controllerInstance == "function") {
  12.     controllerInstance = Reflect.construct(controllerInstance, []);
  13.   }
  14.   if (
  15.     typeof controllerInstance == "object" &&
  16.     typeof controllerInstance.constructor != "function"
  17.   ) {
  18.     throw `控制器不是一个类函数,注册失败`;
  19.   }
  20.   let { prefix, routers }: IControllerMetate = getControllerMeta(
  21.     controllerInstance.constructor
  22.   );
  23.   for (let key in routers) {
  24.     if (key == "constructor") return;
  25.     let routerMeat = routers[key];
  26.     let pathname;
  27.     if (routerMeat.path instanceof RegExp) {
  28.       pathname = routerMeat.path;
  29.     } else if (typeof routerMeat.path !== "object") {
  30.       pathname =
  31.         path
  32.           .join("/", prefix || "", routerMeat.path || "")
  33.           .replace(/\\+/g, "/") || "/";
  34.     }
  35.     //方法参数
  36.     let parameterMeta =
  37.       Reflect.getMetadata(PARAMS_META_KEY, controllerInstance, key) || [];
  38.     let actionFn = async (ctx: any, next: Function) => {
  39.       let args = parameterMeta.map((item: Function) =>
  40.         item(ctx, next, controllerInstance, key)
  41.       );
  42.       const resBody = await (controllerInstance as any)[key].call(
  43.         controllerInstance,
  44.         ...args
  45.       );
  46.       if (resBody !== undefined) ctx.body = await resBody;
  47.       await next();
  48.     };
  49.     let methond = routerMeat.methond;
  50.     routerIntance[methond](pathname, actionFn);
  51.   }
  52. }
复制代码
测试用例
  1. import koaRouter from "koa-router";
  2. import Koa from "koa";
  3. import { Controller, Ctx, GET, Query, ResigerRouter } from "./koa-decorator";
  4. const app = new Koa();
  5. let rotuer = new koaRouter({
  6.   prefix: "/api",
  7. });
  8. @Controller("user")
  9. class User {
  10.   @GET("list")
  11.   getUserId(@Query("id") id: string, @Ctx() ctx: Koa.DefaultContext): string {
  12.     console.log("ctx", ctx);
  13.     return this.computed(id);
  14.   }
  15.   computed(value: string): string {
  16.     return value + "---computed";
  17.   }
  18. }
  19. // 加载路由
  20. ResigerRouter(rotuer, User);
  21. app.use(rotuer.routes());
  22. app.listen(8080, () => {
  23.   console.log("server is run in prot 8080");
  24. });
  25. // 访问:http://127.0.0.1:8080/api/user/list?id=1
复制代码
总结

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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册