Angular下MSAL实例工厂注入ConfigurationService报undefined如何解决
问题根因
ConfigurationService注册语法错误:你将它和APP_INITIALIZER、HTTP_INTERCEPTORS嵌套在了同一个provider对象中,没有完成服务的正确注册,Angular无法识别该服务。- MSAL工厂未声明依赖:
MSAL_INSTANCE对应的useFactory没有配置deps依赖数组,Angular不知道需要给该工厂函数注入ConfigurationService,所以参数默认是undefined。 - 异步加载顺序冲突:你通过
APP_INITIALIZER异步加载配置,但MSAL_INSTANCE的初始化时机早于APP_INITIALIZER的异步执行时机,即使解决前两个问题,也会出现配置未加载完成就读取属性的报错。 - 额外语法问题:
ConfigurationService中的configData为private私有属性,外部无法直接访问,你代码中调用的configurationService.config.clientId缺少对应的getter实现。
修复步骤
1. 修正服务注册与依赖声明
先修正app.module.ts中providers数组的语法错误,同时给MSAL工厂添加依赖声明:
@NgModule({ declarations: [ AppComponent, ... ], imports: [ HttpClientModule, MsalModule, ... ], providers: [ // 单独注册ConfigurationService ConfigurationService, // 单独注册APP_INITIALIZER { provide: APP_INITIALIZER, useFactory: (configService: ConfigurationService) => () => configService.loadConfigurationData(), deps: [ConfigurationService], multi: true }, // 单独注册Msal拦截器 { provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, multi: true }, // 给MSAL_INSTANCE工厂添加deps依赖声明 { provide: MSAL_INSTANCE, useFactory: MSALInstanceFactory, deps: [ConfigurationService] }, { provide: MSAL_GUARD_CONFIG, useFactory: MSALGuardConfigFactory }, { provide: MSAL_INTERCEPTOR_CONFIG, useFactory: MSALInterceptorConfigFactory }, ... ], })
2. 给ConfigurationService添加配置getter
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Configuration } from '../configuration/configuration'; @Injectable() export class ConfigurationService { private configData: Configuration; constructor(private httpClient: HttpClient) { } async loadConfigurationData() : Promise<Configuration>{ // 你的配置加载逻辑 this.configData = await this.httpClient.get<Configuration>('/assets/config.json').toPromise(); return this.configData; } // 新增getter暴露配置数据 get config(): Configuration { return this.configData; } }
3. 解决异步加载顺序问题
因为MSAL实例默认同步初始化,早于APP_INITIALIZER的异步配置加载完成时间,推荐修改为在main.ts中提前加载配置后再启动应用:
// main.ts import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { ConfigurationService } from './app/services/configuration.service'; import { importProvidersFrom } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; // 先加载配置再启动Angular应用 platformBrowserDynamic([ importProvidersFrom(HttpClientModule), ConfigurationService ]) .injector.get(ConfigurationService) .loadConfigurationData() .then((config) => { platformBrowserDynamic([ { provide: 'APP_CONFIG', useValue: config } ]) .bootstrapModule(AppModule) .catch(err => console.error(err)); });
之后修改MSAL工厂直接注入已经加载完成的配置常量:
import { Inject } from '@angular/core'; import { Configuration } from '../configuration/configuration'; export function MSALInstanceFactory(@Inject('APP_CONFIG') config: Configuration): IPublicClientApplication { return new PublicClientApplication({ auth: { clientId: config.clientId // 其余配置项 } }); }
同步修改MSAL_INSTANCE的provider依赖:
{ provide: MSAL_INSTANCE, useFactory: MSALInstanceFactory, deps: ['APP_CONFIG'] }
内容的提问来源于stack exchange,提问作者Agnete
相关产品推荐
相关产品推荐

