在构建自己的 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 容器实现依赖注入。
评论