前言
Nest框架是一个当下流行的,高效可扩展的Node框架。与Java中大名鼎鼎的Spring框架原理相似,Nest使用了控制反转(InverseOfControl,IoC)与依赖注入(Dependency Injection,DI)原则,对复杂工程进行解耦,提倡切面编程。
一、什么是IoC,DI与AOP?
1、控制反转IoC
在传统开发中,类的实例化一般是由开发者完成的,开发者可以根据自身业务需求来实例化自己需要的类,使用其功能。这时,实例化的控制权在开发者手里,但是带来的问题是开发者可能实例化了过多的实例,并且管理较为混乱。相反,控制反转就是将类实例化的权利上交给第三方框架,让框架来帮助开发者实例化对象并进行管理,开发者在需要使用时只需要向IoC容器"伸手"要就行了,不再需要关注类之间的依赖关系。
2、依赖注入DI
当使用IoC的思想进行开发时,开发者是不需要手动实例化当前类中的依赖的,IoC容器帮开发者实例化当前类中的依赖便称为依赖注入。
3、面向切面编程(Aspect Oriented Programming,AOP)
面向切面编程是面向对象编程(OOP)的一种发展。在面向对象编程中,我们可以通过继承来获取父类中的属性和方法,再进行功能扩展。但OOP也会出现一些耦合度高的问题,而AOP的出现就是为了解决高耦合度的问题,如:Dog和Cat类都继承于Animal类,拥有了eat和run方法。
但有时候在分别对eat和run方法进行处理时很可能会出现一些重合的逻辑代码:
class Animal {
eat() {
// 性能监控代码
console.log("I can eat...");
// 业务逻辑代码
console.log("I can eat...");
// 性能监控代码
console.log("I can eat...");
}
run() {
// 性能监控代码
console.log("I can run...");
// 业务逻辑代码
console.log("I can run...");
// 性能监控代码
console.log("I can run...");
}
}
这些重复的逻辑代码称为:"横切逻辑代码",而AOP则负责将这些横切代码抽离出来,降低代码的耦合性。
介绍了Nest框架使用的设计思想,接下来看看这些设计思想在Nest中的实现。不过在这之前,我们需要先具备一些相关知识储备。
二、Reflect Metadata
元数据反射(Reflect Metadata)是ES7的提案,用于存储,读取对象的元数据。
首先介绍反射的概念,反射是指在运行时获取对象的相关数据(属性和方法等)。由于TS类型存在于静态编译,而运行时怎么获取相关依赖的类型信息呢,原因就在于依赖反射的动态类型反推导。
使用Reflect Metadata
要使用Reflect Metadata需要安装一个polifill包
npm i reflect-metadata --save
定义元数据
Reflect.defineMetadata(metadataKey, data, target)
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey)
获取元数据
Reflect.getMetadata(metadataKey, target)
Reflect.getMetadata(metadataKey, target, propertyKey);
这里的target可以是对象也可以是class或者是对象的某个属性,同时还支持以装饰器的方式使用
@Reflect.metadata(metadataKey, metadataValue)
class C {
@Reflect.metadata(metadataKey, metadataValue)
method() {
}
}
我们来看看Reflect.getMetadata在Nest中的简单使用:
import 'reflect-metadata';
class A {
sayHi() {
console.log('hi');
}
}
class B {
sayHello() {
console.log('hello');
}
}
function Module(metadata) {
return (target) => {
for (const property in metadata) {
if (metadata.hasOwnProperty(property)) {
Reflect.defineMetadata(property, metadata[property], target);
}
}
};
}
@Module({
controllers: [B],
providers: [A],
})
class C {}
const providers = Reflect.getMetadata('providers', C);
const controllers = Reflect.getMetadata('controllers', C);
console.log(providers, controllers); // [ [class A] ] [ [class B] ]
通过Reflect拿到了C的controllers和providers。
使用反射推荐搭配TS一期使用,并在tsconfig.json文件里配置emitDecoratorMetadata为true。
配置该选项后,TS在编译时会向使用装饰器的对象里注入3组元数据:
design:type:成员类型design:paramtypes:成员所有参数类型design:returntype:成员返回类型
可以看到TS向相关对象里注入了相关的元数据,这样就可以利用这些元数据来完成依赖注入了。
接下来,我们来看看Nest中的源码具体是怎么做到IoC的。
三、Nest的源码实现
1、NestApplication的创建逻辑
NestApplication是整个Nest服务的代理对象类,在入口文件main.ts,创建了这个类的实例:
const app = await NestFactory.create(AppModule);
我们来看一下这个create方法的具体实现:
public async create<T extends INestApplication = INestApplication>(
module: any,
serverOrOptions?: AbstractHttpAdapter | NestApplicationOptions,
options?: NestApplicationOptions,
): Promise<T> {
const [httpServer, appOptions] = this.isHttpServer(serverOrOptions)
? [serverOrOptions, options]
: [this.createHttpAdapter(), serverOrOptions];
const applicationConfig = new ApplicationConfig();
// 1. 实例化IoC容器,这个容器就是用来存放实例对象的地方
const container = new NestContainer(applicationConfig);
this.setAbortOnError(serverOrOptions, options);
this.registerLoggerConfiguration(appOptions);
// 2. 初始化逻辑,这里实现了依赖注入
await this.initialize(module, container, applicationConfig, httpServer);
// 3. 实例化NestApplication类
const instance = new NestApplication(
container,
httpServer,
applicationConfig,
appOptions,
);
const target = this.createNestInstance(instance);
// 4. 生成一个Proxy代理对象,将对NestApplication实例上部分属性的访问代理到httpServer
return this.createAdapterProxy<T>(target, httpServer);
}
上面看到实例化了一个IoC容器,这里便存放了相关依赖的实例对象,来看看里面的实现:
2、NestContainer,IoC容器
IoC容器里的私有属性modules保存了当前的Module对象,它所依赖的provider和controller等都存放在对应的module对象里。
可以看见modules属性为Map的一个子类。
了解了IoC容器后,我们来看看依赖注入过程:
3、依赖注入
initialize方法
首先我们来看NestFactory.create方法里面的initialize方法。
private async initialize(
module: any,
container: NestContainer,
config = new ApplicationConfig(),
httpServer: HttpServer = null,
) {
// 1. 实例加载器
const instanceLoader = new InstanceLoader(container);
const metadataScanner = new MetadataScanner();
// 2. 依赖扫描器
const dependenciesScanner = new DependenciesScanner(
container,
metadataScanner,
config,
);
container.setHttpAdapter(httpServer);
const teardown = this.abortOnError === false ? rethrow : undefined;
await httpServer?.init();
try {
this.logger.log(MESSAGES.APPLICATION_START);
await ExceptionsZone.asyncRun(
async () => {
// 3. 扫描依赖
await dependenciesScanner.scan(module);
// 4. 生成依赖的实例
await instanceLoader.createInstancesOfDependencies();
dependenciesScanner.applyApplicationProviders();
},
teardown,
this.autoFlushLogs,
);
} catch (e) {
this.handleInitializationError(e);
}
}
initialize方法主要干了三件事:
- 创建Container的实例加载器
- 进行依赖扫描
- 生成依赖实例
将container作为参数传给实例加载器,之后可以为container相关的依赖创建实例,具体的创建过程我们之后再看,现在先看看依赖扫描器是如何进行依赖扫描的。
依赖扫描
代码中调用了dependenciesScanner的scan方法,下面是scan方法的内容:
public async scan(module: Type<any>) {
// 1. 把一些框架内建的module添加到IoC容器中
await this.registerCoreModule();
// 2. 把传入的module添加到IoC容器中
await this.scanForModules(module);
// 3. 扫描当前IoC容器中所有module的依赖
await this.scanModulesForDependencies();
this.calculateModulesDistance();
this.addScopedEnhancersMetadata();
this.container.bindGlobalScope();
}
scan方法中对module中的依赖进行了扫描,其主要过程在this.scanModulesForDependencies()方法中实现:
async scanModulesForDependencies(modules = this.container.getModules()) {
for (const [token, { metatype }] of modules) {
// 依次获取当前module的imports, providers, controllers, exports
await this.reflectImports(metatype, token, metatype.name);
this.reflectProviders(metatype, token);
this.reflectControllers(metatype, token);
this.reflectExports(metatype, token);
}
}
scanModulesForDependencies方法获取了当前module的imports, providers, controllers, exports这些元数据,以providers为例我们可以看到reflectProviders方法调用了this.reflectMetadata方法来获取providers元数据。
reflectProviders(module, token) {
const providers = [
...this.reflectMetadata(constants_1.MODULE_METADATA.PROVIDERS, module),
...this.container.getDynamicMetadataByToken(token, constants_1.MODULE_METADATA.PROVIDERS),
];
providers.forEach(provider => {
this.insertProvider(provider, token);
this.reflectDynamicMetadata(provider, token);
});
}
让我们看看this.reflectMetadata方法内部是什么。
reflectMetadata(metadataKey, metatype) {
return Reflect.getMetadata(metadataKey, metatype) || [];
}
我们看到它使用的就是Reflect.getMetadata方法来获取providers的元数据,这里的元数据正是使用@Module装饰器传入的元数据,获取到providers元数据后调用了this.insertProvider(provider, token)方法将providers元数据添加到了IoC Container中。
完成了Module的依赖扫描,接下来就是要实例化这些依赖了。这里就用到了我们之前的实例加载器instanceLoader。
实例化依赖
在initialize方法中调用了instanceLoader.createInstancesOfDependencies()方法来实例化依赖项,下面是createInstancesOfDependencies()方法的实现:
async createInstancesOfDependencies(modules = this.container.getModules()) {
this.createPrototypes(modules);
await this.createInstances(modules);
}
这里的代码很简洁,主要就是调用了this.createInstances(modules)方法。
async createInstances(modules) {
await Promise.all([...modules.values()].map(async (moduleRef) => {
await this.createInstancesOfProviders(moduleRef);
await this.createInstancesOfInjectables(moduleRef);
await this.createInstancesOfControllers(moduleRef);
const { name } = moduleRef.metatype;
this.isModuleWhitelisted(name) &&
this.logger.log((0, messages_1.MODULE_INIT_MESSAGE) `${name}`);
}));
}
在this.createInstances(modules)方法,可以看到是按照Providers,Injectables,Controllers的顺序进行实例化。
以Providers为例,我们看看实例化的过程:
async createInstancesOfProviders(moduleRef) {
const { providers } = moduleRef;
const wrappers = [...providers.values()];
await Promise.all(wrappers.map(item => this.injector.loadProvider(item, moduleRef)));
}
这里又通过调用Injector类来进行实例化。
这个类里面的具体代码我们不再关注了,我们只看一些和实例化有关的核心代码。
在Injector类的loadInstance方法里调用了this.resolveConstructorParams()方法:
这个方法是对构造函数中的参数进行解析,创建实例,然后将创建好的实例传给了回调函数callback中,执行this.instantiateClass函数实例化Provider对象。
这里需要关注的是this.resolveConstructorParams()方法中依赖的获取与实例化:
async resolveConstructorParams(wrapper, moduleRef, inject, callback, contextId = constants_2.STATIC_CONTEXT, inquirer, parentInquirer) {
let inquirerId = this.getInquirerId(inquirer);
const metadata = wrapper.getCtorMetadata();
if (metadata && contextId !== constants_2.STATIC_CONTEXT) {
const deps = await this.loadCtorMetadata(metadata, contextId, inquirer, parentInquirer);
return callback(deps);
}
const isFactoryProvider = !(0, shared_utils_1.isNil)(inject);
// 获取依赖项
const [dependencies, optionalDependenciesIds] = isFactoryProvider
? this.getFactoryProviderDependencies(wrapper)
: this.getClassDependencies(wrapper);
let isResolved = true;
const resolveParam = async (param, index) => {
try {
if (this.isInquirer(param, parentInquirer)) {
return parentInquirer && parentInquirer.instance;
}
if ((inquirer === null || inquirer === void 0 ? void 0 : inquirer.isTransient) && parentInquirer) {
inquirer = parentInquirer;
inquirerId = this.getInquirerId(parentInquirer);
}
const paramWrapper = await this.resolveSingleParam(wrapper, param, { index, dependencies }, moduleRef, contextId, inquirer, index);
// 将依赖实例化
const instanceHost = paramWrapper.getInstanceByContextId(this.getContextId(contextId, paramWrapper), inquirerId);
if (!instanceHost.isResolved && !paramWrapper.forwardRef) {
isResolved = false;
}
return instanceHost === null || instanceHost === void 0 ? void 0 : instanceHost.instance;
}
catch (err) {
const isOptional = optionalDependenciesIds.includes(index);
if (!isOptional) {
throw err;
}
return undefined;
}
};
const instances = await Promise.all(dependencies.map(resolveParam));
// 传入callback
isResolved && (await callback(instances));
}
将实例化的依赖传入了回调函数callback,回调函数中调用this.instantiateClass函数。
在this.instantiateClass函数中将这些依赖项实例,作为参数传入Provider构造函数中,实例化Provider返回。
4、拦截器(Interceptor)
Interceptor装饰器的使用
这里我们来介绍一下Nest中拦截器创建的过程。在Nest中我们通过@UseInterceptors拦截器给当前Controller或者给某个路由添加拦截器:
@UseInterceptors(SomeInterceptor)
@Controller('add')
export class Controller {
...
}
@UseInterceptors(SomeInterceptor)
async list(
@Req() req: Request,
): Promise<Response> {
...
}
那就先看看UseInterceptors这个装饰器到底干了什么。
export function UseInterceptors(
...interceptors: (NestInterceptor | Function)[]
): MethodDecorator & ClassDecorator {
return (
target: any,
key?: string | symbol,
descriptor?: TypedPropertyDescriptor<any>,
) => {
const isInterceptorValid = <T extends Function | Record<string, any>>(
interceptor: T,
) =>
interceptor &&
(isFunction(interceptor) ||
isFunction((interceptor as Record<string, any>).intercept));
if (descriptor) {
validateEach(
target.constructor,
interceptors,
isInterceptorValid,
'@UseInterceptors',
'interceptor',
);
extendArrayMetadata(
INTERCEPTORS_METADATA,
interceptors,
descriptor.value,
);
return descriptor;
}
validateEach(
target,
interceptors,
isInterceptorValid,
'@UseInterceptors',
'interceptor',
);
// 核心代码
extendArrayMetadata(INTERCEPTORS_METADATA, interceptors, target);
return target;
};
这里的核心代码就是利用Reflect给target类或者target方法注入了INTERCEPTORS_METADATA("interceptors")及interceptors:
export function extendArrayMetadata<T extends Array<unknown>>(
key: string,
metadata: T,
target: Function,
) {
const previousValue = Reflect.getMetadata(key, target) || [];
const value = [...previousValue, ...metadata];
Reflect.defineMetadata(key, value, target);
}
这里给target类或者target方法注入了拦截器元数据后,在之后的Controller实例的创建过程中可以拿到拦截器进行相关操作了。
那么Nest具体是如何实现拦截器的获取与应用呢,这里Nest使用了生产消费模式,定义了两个类:InterceptorsContextCreator与InterceptorsConsumer。
Interceptor的创建
Nest使用RouterExecutionContext类来创建Controller的。
在这个类里面InterceptorsContextCreator类负责创建Interceptor实例以及其所需的context。
InterceptorsConsumer负责使用当前类的Interceptor。
export class RouterExecutionContext {
private readonly handlerMetadataStorage = new HandlerMetadataStorage();
private readonly contextUtils = new ContextUtils();
private readonly responseController: RouterResponseController;
constructor(
private readonly paramsFactory: IRouteParamsFactory,
private readonly pipesContextCreator: PipesContextCreator,
private readonly pipesConsumer: PipesConsumer,
private readonly guardsContextCreator: GuardsContextCreator,
private readonly guardsConsumer: GuardsConsumer,
private readonly interceptorsContextCreator: InterceptorsContextCreator, // 创建拦截器
private readonly interceptorsConsumer: InterceptorsConsumer, // 使用拦截器
readonly applicationRef: HttpServer,
) {
this.responseController = new RouterResponseController(applicationRef);
}
public create(
instance: Controller,
callback: (...args: any[]) => unknown,
methodName: string,
moduleKey: string,
requestMethod: RequestMethod,
contextId = STATIC_CONTEXT,
inquirerId?: string,
) {
const contextType: ContextType = 'http';
const {
argsLength,
fnHandleResponse,
paramtypes,
getParamsMetadata,
httpStatusCode,
responseHeaders,
hasCustomHeaders,
} = this.getMetadata(
instance,
callback,
methodName,
moduleKey,
requestMethod,
contextType,
);
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
getParamsMetadata(moduleKey, contextId, inquirerId),
paramtypes,
);
const pipes = this.pipesContextCreator.create(
instance,
callback,
moduleKey,
contextId,
inquirerId,
);
const guards = this.guardsContextCreator.create(
instance,
callback,
moduleKey,
contextId,
inquirerId,
);
// 创建当前类的拦截器实例
const interceptors = this.interceptorsContextCreator.create(
instance,
callback,
moduleKey,
contextId,
inquirerId,
);
const fnCanActivate = this.createGuardsFn(
guards,
instance,
callback,
contextType,
);
const fnApplyPipes = this.createPipesFn(pipes, paramsOptions);
const handler =
<TRequest, TResponse>(
args: any[],
req: TRequest,
res: TResponse,
next: Function,
) =>
async () => {
fnApplyPipes && (await fnApplyPipes(args, req, res, next));
return callback.apply(instance, args);
};
return async <TRequest, TResponse>(
req: TRequest,
res: TResponse,
next: Function,
) => {
const args = this.contextUtils.createNullArray(argsLength);
fnCanActivate && (await fnCanActivate([req, res, next]));
this.responseController.setStatus(res, httpStatusCode);
hasCustomHeaders &&
this.responseController.setHeaders(res, responseHeaders);
// interceptorsConsumer应用当前类的拦截器
const result = await this.interceptorsConsumer.intercept(
interceptors,
[req, res, next],
instance,
callback,
handler(args, req, res, next),
contextType,
);
await (fnHandleResponse as HandlerResponseBasicFn)(result, res, req);
};
}
知道了主要流程,我们去InterceptorsContextCreator类中看看具体如何创建拦截器实例的:
export class InterceptorsContextCreator extends ContextCreator {
private moduleContext: string;
constructor(
private readonly container: NestContainer,
private readonly config?: ApplicationConfig,
) {
super();
}
public create(
instance: Controller,
callback: (...args: unknown[]) => unknown,
module: string,
contextId = STATIC_CONTEXT,
inquirerId?: string,
): NestInterceptor[] {
this.moduleContext = module;
return this.createContext(
instance,
callback,
INTERCEPTORS_METADATA,
contextId,
inquirerId,
);
}
public createConcreteContext<T extends any[], R extends any[]>(
metadata: T,
contextId = STATIC_CONTEXT,
inquirerId?: string,
): R {
if (isEmpty(metadata)) {
return [] as R;
}
return iterate(metadata)
.filter(
interceptor =>
interceptor && (interceptor.name || interceptor.intercept),
)
.map(interceptor =>
this.getInterceptorInstance(interceptor, contextId, inquirerId),
)
.filter(
(interceptor: NestInterceptor) =>
interceptor && isFunction(interceptor.intercept),
)
.toArray() as R;
}
public getInterceptorInstance(
metatype: Function | NestInterceptor,
contextId = STATIC_CONTEXT,
inquirerId?: string,
): NestInterceptor | null {
const isObject = (metatype as NestInterceptor).intercept;
if (isObject) {
return metatype as NestInterceptor;
}
const instanceWrapper = this.getInstanceByMetatype(
metatype as Type<unknown>,
);
if (!instanceWrapper) {
return null;
}
const instanceHost = instanceWrapper.getInstanceByContextId(
contextId,
inquirerId,
);
return instanceHost && instanceHost.instance;
}
public getInstanceByMetatype(
metatype: Type<unknown>,
): InstanceWrapper | undefined {
if (!this.moduleContext) {
return;
}
const collection = this.container.getModules();
const moduleRef = collection.get(this.moduleContext);
if (!moduleRef) {
return;
}
return moduleRef.injectables.get(metatype);
}
public getGlobalMetadata<T extends unknown[]>(
contextId = STATIC_CONTEXT,
inquirerId?: string,
): T {
if (!this.config) {
return [] as T;
}
const globalInterceptors = this.config.getGlobalInterceptors() as T;
if (contextId === STATIC_CONTEXT && !inquirerId) {
return globalInterceptors;
}
const scopedInterceptorWrappers =
this.config.getGlobalRequestInterceptors() as InstanceWrapper[];
const scopedInterceptors = iterate(scopedInterceptorWrappers)
.map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
.filter(host => !!host)
.map(host => host.instance)
.toArray();
return globalInterceptors.concat(scopedInterceptors) as T;
}
}
代码结构比较清楚,是通过调用create方法来创建实例,该方法中调用了父类ContextCreator中的createContext方法,我们来看看该方法:
public createContext<T extends unknown[] = any, R extends unknown[] = any>(
instance: Controller,
callback: (...args: any[]) => void,
metadataKey: string,
contextId = STATIC_CONTEXT,
inquirerId?: string,
): R {
// 获取全局Metadata
const globalMetadata =
this.getGlobalMetadata &&
this.getGlobalMetadata<T>(contextId, inquirerId);
// 获取类的Metadata
const classMetadata = this.reflectClassMetadata<T>(instance, metadataKey);
// 获取方法的Metadata
const methodMetadata = this.reflectMethodMetadata<T>(callback, metadataKey);
// 将全局的,类的,方法的Metadata传入createConcreteContext方法进行实例化
return [
...this.createConcreteContext<T, R>(
globalMetadata || ([] as T),
contextId,
inquirerId,
),
...this.createConcreteContext<T, R>(classMetadata, contextId, inquirerId),
...this.createConcreteContext<T, R>(
methodMetadata,
contextId,
inquirerId,
),
] as R;
}
可以看出该方法获取了全局的,类的,方法的Metadata并将其传入createConcreteContext方法进行实例化。
通过上面的代码可以清楚看到getInterceptorInstance方法就是用来实例化拦截器的。
该方法参数metatype接受两种参数Function与NestInterceptor,传入NestInterceptor类对应的是Function,另一种则是传入NestInterceptor的实例对象:
// Function类型
@UseInterceptors(SomeInterceptor)
fn() {}
// NestInterceptor类型
@UseInterceptors(new SomeInterceptor())
fn() {}
在getInterceptorInstance方法中,metatype若是NestInterceptor类型则直接返回该实例。
metatype若是Function类型,则调用getInstanceByMetatype方法获取实例,而getInstanceByMetatype方法是通过当前moduleRef.injectables获取。
这说明拦截器是保存在对应module的injectables里的,那是什么时候实例化并保存在moduleRef.injectables里的呢?
为了找到源头,我们顺藤摸瓜去找module注入injectable元数据的地方。
在依赖扫描DependenciesScanner的类中,我们找到了注入injectable元数据的地方:
public reflectControllers(module: Type<any>, token: string) {
const controllers = [
...this.reflectMetadata(MODULE_METADATA.CONTROLLERS, module),
...this.container.getDynamicMetadataByToken(
token,
MODULE_METADATA.CONTROLLERS as 'controllers',
),
];
controllers.forEach(item => {
this.insertController(item, token);
this.reflectDynamicMetadata(item, token);
});
}
public reflectDynamicMetadata(obj: Type<Injectable>, token: string) {
if (!obj || !obj.prototype) {
return;
}
this.reflectInjectables(obj, token, GUARDS_METADATA);
this.reflectInjectables(obj, token, INTERCEPTORS_METADATA);
this.reflectInjectables(obj, token, EXCEPTION_FILTERS_METADATA);
this.reflectInjectables(obj, token, PIPES_METADATA);
this.reflectParamInjectables(obj, token, ROUTE_ARGS_METADATA);
}
可以看到在获取Controller的时候通过reflectDynamicMetadata方法将Guard,Interceptor,ExceptionFilter,Pipe以及ROUTE_ARGS作为Injectable类型的元数据注入了Controller中。
这样便可以在依赖注入时完成类的实例化,这样就完成了拦截器的创建。
Interceptor的使用(消费)
InterceptorsConsumer负责拦截器的使用:
export class InterceptorsConsumer {
public async intercept<TContext extends string = ContextType>(
interceptors: NestInterceptor[],
args: unknown[],
instance: Controller,
callback: (...args: unknown[]) => unknown,
next: () => Promise<unknown>,
type?: TContext,
): Promise<unknown> {
if (isEmpty(interceptors)) {
return next();
}
const context = this.createContext(args, instance, callback);
context.setType<TContext>(type);
const start$ = defer(() => this.transformDeferred(next));
const nextFn =
(i = 0) =>
async () => {
if (i >= interceptors.length) {
return start$;
}
const handler: CallHandler = {
handle: () => fromPromise(nextFn(i + 1)()).pipe(mergeAll()),
};
return interceptors[i].intercept(context, handler);
};
return nextFn()();
}
}
这里就是从interceptors数组里依次取出全局,类,方法拦截器来执行。
以上就是Nest中拦截器的的创建与使用的整个过程,值得注意的是如果给@UseInterceptors装饰器中传入的是类名,可以借助Nest完成拦截器的中的依赖注入,若传入的是一个NestInterceptor实例,则Nest不会帮你完成依赖注入,需要你手动实例化。