使用装饰器和映射,可以优雅的给 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);
}
}
}用于定义类中的路径前一段
import "reflect-metadata"
export default function Controller(path: string): ClassDecorator {
return (target) => {
Reflect.defineMetadata("path", path, target);
}
}获取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);
});
}
}
}