浅析Nest如何实现装饰器的路由注册
在nest
中你可以看到使用装饰器实现路由路径。
1 | import { Controller, Get, Query } from '@nestjs/common'; |
主要就是由Controller
和其他http-method
装饰器。而实际的路由路径是:
全局前缀(可选) + Controller装饰器的文本参数(可选) + http-method装饰器文本参数(可选)
存储路径数据
Controller前缀的保存
1 | export function Controller( |
在Controller
装饰器工厂中,首先是获取了path, scopeOptions,然后返回了一个装饰器函数。这个函数主要是保存工厂函数的参数。
这里用到了Reflect
,是一个用来存取元数据的库,数据结构主要是Map和WeakMap。
1 | Reflect.defineMetadata(PATH_METADATA, path, target); |
将path这个数据WeakMap/target/PATH_METADATA
中。所以取的时候也需要target
, PATH_METADATA
。
http-method前缀的保存
1 |
|
上面以@Get()
装饰器的实现为例。和@Controller
的实现大同小异,最终都是将工厂函数的参数使用Reflect
保存起来,
1 | Reflect.defineMetadata(PATH_METADATA, path, descriptor.value); |
分别将请求方法和路径保存了起来。
这里就有个问题:为什么要先保存这些路由数据,而不是直接使用?
首先,装饰器实际发挥作用的阶段是紧接着类声明之后,立即就执行装饰器函数。而业务逻辑是在访问路由之后才会被触发。
小结
- 使用Reflect存放元数据,Reflect由一个外部库reflect-metadata实现;
- target(类的引用,也可以说是构造函数)/PATH_METADATA存放
Controller
部分的路径数据; - descriptor.value(成员方法的引用,即方法本身)/PATH_METADATA存放,存放剩余的http-method部分的路径数据;
组合路由的路径数据
下面我们来看nest怎么组合分散的路由路径path
1 | public async registerRouter() { |
这个方法中,获取全局前缀,得到路径的最开头部分:
1 | path = [ |
1 | public resolve<T extends HttpServer>(applicationRef: T, basePath: string) { |
这里组合模块部分的路径:
1 | path = [ |
1 | // 获取Controller的路径 |
组合Controller
部分的路由数据:
1 | path = [ |
1 | public explore<T extends HttpServer = any>( |
最后是剩余的http-method部分路由数据
1 | path = [ |
小结
- Controller和http-method装饰工厂存储的路径数据在注册路由的时候被访问并组合成完成路径;