繁体   English   中英

如何在动态 Nestjs 模块中导入 registerAsync?

[英]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并直接使用registerforRoot方法。 这样,您可以快速轻松地向下传递配置值。 如果您不介意不使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM