我需要编写中间件来处理请求,但是应排除某些路径。我不想手动对它们全部进行硬编码,所以我有一个主意:
创建特殊的装饰器,它将标记要排除的方法,如下所示:
import { ReflectMetadata } from '@nestjs/common';
export const Exclude = () =>
ReflectMetadata('exclude', 'true');
在创建NestJS应用程序之后,是否有办法以某种方式递归地获取所有使用此装饰器注释的方法,以自动添加要排除在中间件中的路径?
答案 0 :(得分:2)
我已经发布了一个可重用的模块,用于在您的处理程序或Injectable类上发现元数据,专门用于支持该模式。您可以从NPM抓取Route::get('/route/path', ['middleware' => 'throttle:2', 'uses' => 'Controller@action']);
,然后使用@nestjs-plus/common
根据您提供的MetaData令牌自动检索所有匹配的处理程序或类。源代码为available on Github。我将在短期内继续更新文档,但是存储库中已经包含了一些示例用法。
在后台,它使用MetaDataScanner,但将其包装在易于使用的API中。查看您发布的代码片段,这可以帮助减少特定用例的大量样板。您可以在DiscoveryService
模块(来自同一存储库)中看到更多高级用法,以了解如何将其用于glue together advanced functionality。
编辑:
我已经更新了该库,以支持用于发现控制器和控制器方法的方案以支持您的方案。 There's a complete test suite that mimics your setup with the @Roles
decorator you can check out.。在您的导入中包含@nestjs-plus/rabbitmq
并注入DiscoveryModule
之后,您可以使用简化的DiscoverService
API找到所有控制器方法。
methodsAndControllerMethodsWithMeta
发现所有方法后,您可以对它们进行任何操作,以您的情况为例,建立它们的// Inject the service
constructor(private readonly discover: DiscoveryService) { }
// Discover all controller methods decorated with guest roles or
// belonging to controllers with guest roles
const allMethods = this.discover.methodsAndControllerMethodsWithMeta<string[]>(
rolesMetaKey,
x => x.includes('guest')
);
和RequestMethod
的集合。
path
这会给你类似的东西(从链接的测试套件中获取)。
const fullPaths = allGuestMethods.map(x => {
const controllerPath = Reflect.getMetadata(
PATH_METADATA,
x.component.metatype
);
const methodPath = Reflect.getMetadata(PATH_METADATA, x.handler);
const methodHttpVerb = Reflect.getMetadata(
METHOD_METADATA,
x.handler
);
return {
verb: methodHttpVerb,
path: `${controllerPath}/${methodPath}`
}
});
随时提供有关方法/ API的反馈。
答案 1 :(得分:0)
所以...帮助自己。
深入研究NestJS来源后,我找到了一种方法,这是对感兴趣的人的指导:
import * as pathToRegexp from 'path-to-regexp';
import { INestApplication, RequestMethod } from '@nestjs/common';
import { NestContainer } from '@nestjs/core/injector/container';
import { MetadataScanner } from '@nestjs/core/metadata-scanner';
import { PATH_METADATA, MODULE_PATH, METHOD_METADATA } from '@nestjs/common/constants';
const trimSlashes = (str: string) => {
if (str != null && str.length) {
while (str.length && str[str.length - 1] === '/') {
str = str.slice(0, str.length - 1);
}
}
return str || '';
};
const joinPath = (...p: string[]) =>
'/' + trimSlashes(p.map(trimSlashes).filter(x => x).join('/'));
// ---------------8<----------------
const app = await NestFactory.create(AppModule);
// ---------------8<----------------
const excludes = Object.create(null);
const container: NestContainer = (app as any).container; // this is "protected" field, so a bit hacky here
const modules = container.getModules();
const scanner = new MetadataScanner();
modules.forEach(({ routes, metatype }, moduleName) => {
let modulePath = metatype ? Reflect.getMetadata(MODULE_PATH, metatype) : undefined;
modulePath = modulePath ? modulePath + globalPrefix : globalPrefix;
routes.forEach(({ instance, metatype }, controllerName) => {
const controllerPath = Reflect.getMetadata(PATH_METADATA, metatype);
const isExcludeController = Reflect.getMetadata('exclude', metatype) === 'true';
const instancePrototype = Object.getPrototypeOf(instance);
scanner.scanFromPrototype(instance, instancePrototype, method => {
const targetCallback = instancePrototype[method];
const isExcludeMethod = Reflect.getMetadata('exclude', targetCallback) === 'true';
if (isExcludeController || isExcludeMethod) {
const requestMethod: RequestMethod = Reflect.getMetadata(METHOD_METADATA, targetCallback);
const routePath = Reflect.getMetadata(PATH_METADATA, targetCallback);
// add request method to map, if doesn't exist already
if (!excludes[RequestMethod[requestMethod]]) {
excludes[RequestMethod[requestMethod]] = [];
}
// add path to excludes
excludes[RequestMethod[requestMethod]].push(
// transform path to regexp to match it later in middleware
pathToRegexp(joinPath(modulePath, controllerPath, routePath)),
);
}
});
});
});
// now you can use `excludes` map in middleware