[英]Can't inject service from my dynamic module into "registerAsync" of another module in NestJS
[英]How to import a registerAsync in a dynamic Nestjs module?
一直在研究使用 Advanced NestJS 的动态模块: 如何构建完全动态的 NestJS 模块。
据我所知,大多数人使用本指南来构建同步/异步动态模块。
但我的问题是,如果我使用 registerAsync 方法,我的动态模块需要导入 HttpModule,而 HttpModule 的 register-options 由我的动态模块提供。
如何在动态模块中导入模块,其中选项由动态模块提供? 或者这是处理这个问题的错误方法? 如果是这样,您将如何构建它?
这是代码。 这实际上是本教程的抄本。 正如您在 register 方法中看到的那样,它很简单 - 我只是传入选项。 registerAsync 但是,我无法弄清楚该怎么做。
任何帮助深表感谢:)
import { Module, DynamicModule, Provider, HttpModule } from "@nestjs/common";
import { InvoicesHealth } from "./invoices/invoices.health";
import { InvoicesResolver, InvoicesService } from "./invoices";
import {
CustomerInvoicesOptions,
CustomerInvoicesAsyncOptions,
CustomerInvoicesOptionsFactory,
} from "./interfaces";
import { CUSTOMER_INVOICES_OPTIONS } from "./constants";
import { createCustomerInvoicesProviders } from "./providers/customer-invoices.providers";
@Module({
imports: [],
controllers: [],
providers: [InvoicesHealth, InvoicesResolver, InvoicesService],
exports: [InvoicesHealth],
})
export class CustomerInvoicesModule {
/**
* Registers a configured customer-invoices Module for import into the current module
*/
public static register(options: CustomerInvoicesOptions): DynamicModule {
return {
imports: [
HttpModule.register({
url: options.url,
auth: {
username: options.username,
password: options.password,
},
}),
],
module: CustomerInvoicesModule,
providers: createCustomerInvoicesProviders(options),
};
}
/**
* Registers a configured customer-invoices Module for import into the current module
* using dynamic options (factory, etc)
*/
public static registerAsync(
options: CustomerInvoicesAsyncOptions,
): DynamicModule {
return {
module: CustomerInvoicesModule,
imports: options.imports || [],
providers: [...this.createProviders(options)],
};
}
private static createProviders(
options: CustomerInvoicesAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createOptionsProvider(options)];
}
return [
this.createOptionsProvider(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
private static createOptionsProvider(
options: CustomerInvoicesAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: CUSTOMER_INVOICES_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
}
// For useExisting...
return {
provide: CUSTOMER_INVOICES_OPTIONS,
useFactory: async (optionsFactory: CustomerInvoicesOptionsFactory) =>
await optionsFactory.createFtNestCustomerInvoicesOptions(),
inject: [options.useExisting || options.useClass],
};
}
}
好吧,系好安全带,因为没有一个简单的答案,但有一些方法可以解决它。
首先,没有办法从另一个模块的异步注册方法中调用一个模块的异步注册方法。 至少不使用传入的异步配置,所以我将向您展示可以做什么。
imports
可能是仍然适用于异步注册方法的三个选项中最简单的一个。 传递给异步配置的imports
数组在模块中完全可用,因此您最终可以执行类似的操作
CustomerInvoicesModule.registerAsync({
imports: [
HttpModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: httpConfigFactory,
}),
ConfigModule
],
inject: [ConfigService],
useFacotry: customerInvoicesConfigFactory,
})
这将在其完整配置中公开HttpService
。 唯一需要注意/小心的是确保您跟踪注册级别。
asyncConfigInterface
这是一个有趣的。 您可以将异步配置选项更改为类似于
export interface CustomInvoiceModuleAsyncOptions {
http: HttpModuleAsyncOptions;
useClass?: RegularValueHere;
useFacotry?: RegularValueHere;
useValue?: RegularValueHere;
inject: injectionArray;
imports: importsArray;
}
现在在您的registerAsync
方法中,您可以执行
static registerASync(options: CustomerInvoiceModuleAsyncOptions): DynamicModule {
return {
module: CustomerInvoicesModule,
imports: [HttpModule.registerAsync(options.http)]
providers: [...this.createProvider(options)],
exports: [...this.createProvider(options)],
}
}
现在,这意味着在模块的选项中为HttpModule
传递了配置,这看起来很难看,但它可以将选项传递给正确的模块。
register/forRoot
只是不要使用ConfgModule
并直接使用register
或forRoot
方法。 这样,您可以快速轻松地向下传递配置值。 如果您不介意不使用ConfigModule
,这非常简单。
扩展杰伊的答案,可能有一个适合您的选项四。
extraProviders
并非所有模块都支持这一点(但HttpModule
似乎 - 所以你很幸运)。
public static registerAsync(options: CustomerInvoicesAsyncOptions,): DynamicModule {
const providers = this.createProviders(options);
return {
module: CustomerInvoicesModule,
imports: [
HttpModule.registerAsync({
imports: options.imports || [],
// Factory is passed the injected CustomerInvoicesOptions as argument
// which is based on the result of the extra providers
useFactory: async (options: CustomerInvoicesOptions) => ({
url: options.url,
auth: {
username: options.username,
password: options.password,
},
}),
inject: [CUSTOMER_INVOICES_OPTIONS],
extraProviders: providers,
}),
...(options.imports || []),
],
providers: [...providers],
};
}
杰伊可能能够澄清这种方法是否正确,但它似乎对我有用。
@Global()/export
对于模块未在其AsyncOptions
中公开extraProviders
属性的实例,您可以通过创建模块@Global()
并导出CUSTOMER_INVOICES_OPTIONS
提供程序来规避此问题。
例如对于PassportModule
(不幸的是它没有公开extraProviders
属性)
@Global()
@Module({...})
export class CustomerInvoicesModule {
....
public static registerAsync(options: CustomerInvoicesAsyncOptions,): DynamicModule {
return {
module: CustomerInvoicesModule,
imports: [
PassportModule.registerAsync({
useFactory: async (options: CustomerInvoicesOptions) => ({
defaultStrategy: options.oauthEnabled ? 'jwt' : 'no-auth',
}),
inject: [CUSTOMER_INVOICES_OPTIONS],
}),
...(options.imports || []),
],
providers: [...this.createProviders(options)],
exports: [CUSTOMER_INVOICES_OPTIONS],
};
}
....
这种@Global()
方法也有效,但不是特别好。
我尝试过的其他事情都失败了:
forwardRef(() => MyModule)
- 这总是失败, metatype is not a constructor
错误。希望这种见解有所帮助,并且任何 NestJS 专家都可以纠正任何一种方法的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.