在构建自己的 Web 框架时,使用装饰器来定义路由是一种非常优雅且强大的方法。通过装饰器,我们可以在控制器类上指定基础路径,并为每个方法定义具体的路由。接着,我们利用 TypeScript 的反射能力,解析这些装饰器提供的路由信息,并将其映射到 Web 服务器上。
在本篇文章中,我们将展示如何使用 @Controller、@Get 和 @Post 装饰器来定义路由,并通过反射机制将这些路由映射到 hyper-express Web 服务器上。
一、定义装饰器
首先,我们需要定义 @Controller、@Get 和 @Post 装饰器。@Controller 用于标记控制器类,并为其提供一个基础路径。@Get 和 @Post 分别用于标记处理 GET 和 POST 请求的方法。
function Controller(basePath: string) { return function (target: Function) { Reflect.defineMetadata('basePath', basePath, target); };}
function Get(path: string) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const basePath = Reflect.getMetadata('basePath', target.constructor); const fullPath = `${basePath}${path}`; Reflect.defineMetadata('route:get', fullPath, target, propertyKey); };}
function Post(path: string) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const basePath = Reflect.getMetadata('basePath', target.constructor); const fullPath = `${basePath}${path}`; Reflect.defineMetadata('route:post', fullPath, target, propertyKey); };}
复制代码
二、创建控制器类
接下来,我们创建一个控制器类,并使用上面定义的装饰器来定义路由。
@Controller('/api')class MyController { @Get('/hello') public async getHello(req: any, res: any) { res.end('Hello from /api/hello'); }
@Post('/submit') public async postSubmit(req: any, res: any) { res.end('Data submitted to /api/submit'); }}
复制代码
在上面的代码中,MyController 类被 @Controller 装饰器标记,并指定了基础路径 /api。getHello 方法被 @Get 装饰器标记,并指定了路由路径 /hello,因此它的完整路径是 /api/hello。同样地,postSubmit 方法被 @Post 装饰器标记,并指定了路由路径 /submit,完整路径为 /api/submit。
三、解析并映射路由
现在,我们需要编写代码来解析控制器类上的路由信息,并将其映射到 hyper-express Web 服务器上。
import { createServer } from 'hyper-express';
const app = createServer();
function mapRoutes(controller: any) { const basePath = Reflect.getMetadata('basePath', controller);
const methods = Object.getOwnPropertyNames(controller.prototype).filter(methodName => methodName !== 'constructor');
methods.forEach(methodName => { const getPath = Reflect.getMetadata('route:get', controller.prototype, methodName); const postPath = Reflect.getMetadata('route:post', controller.prototype, methodName);
if (getPath) { app.get(getPath, controller.prototype[methodName].bind(controller)); }
if (postPath) { app.post(postPath, controller.prototype[methodName].bind(controller)); } });}
// 假设 MyController 已经定义并可用mapRoutes(MyController);
app.listen(3000, () => { console.log('Server started on port 3000');});
复制代码
在上面的代码中,mapRoutes 函数遍历控制器类原型上的所有方法,并检查是否存在 @Get 或 @Post 装饰器定义的路由路径。如果存在,则使用 app.get 或 app.post 方法将方法映射到相应的路由上。注意,我们使用 bind(controller) 来确保方法中的 this 指向控制器实例。
四、总结
结合前文的文件扫描和自动导入,我们可以轻松的将项目中约定或配置的路径下的控制器文件映射到 Web 服务器的路由系统,并且可以使用 IoC 容器实现依赖注入。
评论