#dependency-injection #nestjs
#внедрение зависимостей #nestjs
Вопрос:
Есть ли способ лучше, чем приведенный ниже, внедрить сервис или компонент внутри импортированного модуля?
export interface AmqpInterceptor{
after(message:any):Promise<void>;
}
export class AmqpInterceptors extends Array<AmqpInterceptor>{
}
//generic library module
@Module({
providers:[{
provide: AmqpInterceptors,
useValue: []
}]
}
export class AMQPModule implements OnModuleInit{
static register(options: AMQPOptions): DynamicModule {
const providers = options.providers || []
return {
module: AMQPModule,
providers: [
...providers,
OtherProvider
]
}
}
}
//end user module
@Module({
imports: [
AMQPModule.register(({
// I had to create a factory method to pass providers as an argument.
// I would think that it is not a good practice
providers: [{
provide:AmqpInterceptors,
useValue:[MyCustomInterceptor]
}]
})
],
providers: [
]
})
export class QueueModule {
}
Текущее рабочее решение: я объявляю пустой массив по умолчанию в универсальном модуле и фабричный метод, который позволяет передавать пользовательское значение при построении модуля.(В моем самом счастливом мире я объявляю несколько экземпляров интерфейса, а затем DI собирает все это, но я думаю, что это действительно невозможно в NestJS)
Комментарии:
1. Вы хотите внедрить сервис в импортированный модуль A, почему бы вам не создать отдельный модуль B, который вы импортируете в A вместо этого?
2. Потому что мне нужно, чтобы модуль A не зависел от какой-либо дополнительной логики. Т.Е. Любой, кто хочет использовать модуль A, должен иметь возможность перехватывать (отслеживать, преобразовывать, регистрировать, трассировать и т.д.) Сообщения. И к тому времени, когда я опубликую «модуль A», этот код даже не будет существовать
Ответ №1:
вы можете использовать пакет, подобный @golevelup / nestjs-discovery, чтобы помочь вам.
В основном вы должны сделать следующее:
// Модуль AMQP
// amqp-interceptor.decorator.ts
import { SetMetadata } from '@nestjs/common';
export function AmqpInterceptor() {
return SetMetadata('AMQP_INTERCEPTOR', true)
}
// amqp-explorer.ts
import { OnModuleInit } from '@nestjs/common'
@Injectable()
export class AmqpExplorer implements OnModuleInit {
constructor(
private readonly discoveryService: DiscoveryService,
) {}
async onModuleInit(): Promise<void> {
const amqpInterceptorProviders = await this.discoveryService.providers('AMQP_INTERCEPTOR')
// you can store this list, to be queried by some
// other provider to use interceptors, etc.
}
}
// amqp.module.ts
@Module({ providers:[AmqpExplorer]})
export class AMQPModule { }
// модуль конечного пользователя
// end-user.module.ts
@Module({ imports:[AMQPModule], providers: [SomeAmqpInterceptor] })
export class EndUserModule { }
// some-amqp.interceptor.ts
@Injectable()
@AmqpInterceptor()
export class SomeAmqpInterceptor {
run(): void { console.log('intercepting') }
}
Obs:
- Декоратор перехватчика: вы можете добавить любые параметры, которые помогли бы вам улучшить ваш api
- Я предлагаю это (список поставщиков) вместо внедрения оформленных поставщиков в ваш модуль, потому что я не знаю, как вы планируете их использовать. Таким образом, у вас есть список поставщиков, и вы можете вызывать каждого из них.
AMQP_INTERCEPTOR
строка может конфликтовать с некоторыми другими метаданными, рекомендуется добавлять что-то более конкретное, чтобы избежать двух одинаковых метаданных в модуле- Для последующего вызова перехватчиков:
import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator'
export class Runner {
constructor(
private readonly handler: DiscoveredMethodWithMeta,
private readonly externalContextCreator: ExternalContextCreator,
) { }
run(): void {
const handler = this.externalContextCreator.create(
this.handler.discoveredMethod.parentClass.instance,
this.handler.discoveredMethod.handler,
this.handler.discoveredMethod.methodName,
)
const handlerResult = await handler(content)
}
- Не запускался локально, но я думаю, что это хороший способ начать
Комментарии:
1. Я думаю, что это блестящее решение. Думаю, я могу даже добавить метаданные приоритета (например, Spring). Большое вам спасибо.
2. Да, все зависит от ваших требований. И если это решит ваш вопрос, отметьте как ответ, пожалуйста: D