Skip to content

Instantly share code, notes, and snippets.

@Houserqu
Created May 5, 2021 01:51
Show Gist options
  • Save Houserqu/c8b5e1044d2d3094359cb81565a51c97 to your computer and use it in GitHub Desktop.
Save Houserqu/c8b5e1044d2d3094359cb81565a51c97 to your computer and use it in GitHub Desktop.
装饰器路由实现

使用装饰器和映射,可以优雅的给 Controller 中的方法绑定路由

方法装饰器

用于定义路由路径和请求类型

import 'reflect-metadata'

type Method = 'get' | 'post' |'put'

export interface DecoratorRoute {
  path: string
  name: string | symbol
  method: Method
}

/**
 * Get 请求装饰器
 * @param path 
 */
export function Get(path: string): MethodDecorator {
  return injectRoute('get', path)
}

/**
 * Put 请求装饰器
 * @param path 
 */
export function Post(path: string): MethodDecorator {
  return injectRoute('post', path)
}

/**
 * 通用注册路由方法
 * 实现原理:通过装饰器获取路径和方法,再通过反射注入到 controlle 到 routes 属性中
 *         在 useController 方法获取所有 controller 的路由参数,配置到 router 中
 * @param method 
 * @param path 
 */
function injectRoute(method: Method, path: string): MethodDecorator {
  return (target, name) => {
    const paths = Reflect.getMetadata('routes', target)
    const route: DecoratorRoute = { path, name, method }

    if(paths && paths instanceof Array) {
      paths.push(route)
    } else {
      Reflect.defineMetadata("routes", [route], target);
    }
  }
}

Controller 装饰器

用于定义类中的路径前一段

import "reflect-metadata"

export default function Controller(path: string): ClassDecorator {
  return (target) => {
    Reflect.defineMetadata("path", path, target);
  }
}

绑定路由和Controller

获取controller中所有方法装饰的路径和请求类型,注册到 Route 对象中

import "reflect-metadata"
import Router from "koa-router";
import { DecoratorRoute } from '../decorator/method'
import path from 'path'

/**
 * 绑定 controller 与路由
 * @param router 
 * @param controllers 
 */
export function useController(router: Router, controllers: any[]): void {
  for (const controller of controllers) {
    // controller 定义的路由前缀
    const pathPre = Reflect.getMetadata("path", controller);

    // controller 中每个方法定义的路由
    const routes: DecoratorRoute[] = Reflect.getMetadata("routes", controller.prototype);
    

    const instance = new controller();
    for (const route of routes) {
      if(process.env.NODE_ENV === 'development') {
        console.log('regist route: ', path.resolve('/', pathPre, route.path))
      }

      // 注册路由
      router[route.method](path.resolve('/', pathPre, route.path), async ctx => {
        ctx.body = await instance[route.name](ctx);
      });
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment