APP_INITIALIZER 和相关令牌解析问题

jaz*_*azz 7 typescript angular

我正在使用 APP_INITIALIZER 从 json 获取配置并且它工作正常。早些时候我有一个身份验证保护作为应用程序的一部分,它曾经很好用。

然后我们将授权逻辑拆分到一个库中,如果我们调用 forRoot() 或者如果我们为 config 提供静态值但允许动态配置,我使用库中的 InjectionToken 来提供配置而不调用 forRoot,它可以正常工作。

app.module.ts的代码是这样的:

let authConfig: any;

export function authConfigFactory() {
  return authConfig;
}

export function appInitializerFn(appConfigService: AppConfigService) {
  return () => {
    return appConfigService.loadAppConfig().then(() => {
      authConfig = {
        clientId: appConfigService.getConfig().CLIENT_ID,
        tokenEndpoint: appConfigService.getConfig().TOKEN_URL,
        redirectURL: "http://localhost",
      };
    });
  };
};

@NgModule({
.....
  imports: [
..
AuthLib
],
  providers: [
    AppConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: appInitializerFn,
      multi: true,
      deps: [AppConfigService]
    },
    AuthLibService,
    {
      provide: 'authConfig',
      useFactory: authConfigFactory,
      deps: []
    },
.....
]
bootstrap: [AppComponent]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)

现在authConfigFactoryappInitializerFn之前被调用,导致未定义,如果我将异步添加到authConfigFactory以防止返回语句直到其定义,则空值被提供给 AuthGuard 导致令牌 URL 无效。

如果我在调用 promise 之前在appInitializerFn 中手动提供值,则值会被转换并且一切正常。但在那个阶段,动态值不存在。

app-config.service.ts 的代码:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { AppConfig } from '../_models/app-config';
import { LoggingService } from './logging.service';

@Injectable()
export class AppConfigService {
  static appConfig : AppConfig;
  private dataLoc: string;
  constructor(private http: HttpClient,
              private logger: LoggingService) { }

  loadAppConfig() {
      if(environment.production){
        this.dataLoc = '/assets/data/appConfig.json';
      }
      else{
        this.dataLoc = '/assets/data/appConfig-dev.json';
      }
    return new Promise<void>((resolve, reject) =>  {
      this.http.get(this.dataLoc).toPromise().then((response: AppConfig) => {
        AppConfigService.appConfig = <AppConfig>response;
        resolve();
      }).catch((response: any) => {
        reject(`Could not load file '${this.dataLoc}': 
${JSON.stringify(response)}`);
     });
    });
  }
  getConfig() {
    return AppConfigService.appConfig;
  }
}
Run Code Online (Sandbox Code Playgroud)

库或代码中缺少什么来使这项工作?

我对 Angular 制度有点陌生,即使我犯了一些愚蠢的错误,也请告诉我。

jaz*_*azz 8

于是找到原因和解决办法。为任何人发布它绊倒这个问题。

由于 APP_INITIALIZER 被正确调用。我错过的事情是我正在使用“HttpClient”来获取配置,该配置反过来调用在 ROOT 中定义的任何 HTTP_INTERCEPTORS 并最终导致身份验证服务的初始化,这反过来需要身份验证配置作为构造函数中的令牌注入。

因此,即使在获取值之前,令牌也会被注入,导致它未定义/为空。

解决方案虽然很简单,但我们可以使用

  1. HttpBackend 获取 json 或
  2. 获取 main.ts 中的 json 或
  3. 防止为该 json 路径调用拦截器。

在我的情况下,第 3 点是不可能的,因为我想严格控制所有通信,而第 2 点有点笨拙。我们采用了方法 1。共享修改后的代码以供参考。

import { Injectable } from '@angular/core';
import { HttpClient, HttpBackend } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { AppConfig } from '../_models/app-config';
import { LoggingService } from './logging.service';

@Injectable()
export class AppConfigService {
  static appConfig : AppConfig;
  private dataLoc: string;
  constructor(private handler: HttpBackend,
              private logger: LoggingService) { }

  loadAppConfig() {
      if(environment.production){
        this.dataLoc = '/assets/data/appConfig.json';
      }
      else{
        this.dataLoc = '/assets/data/appConfig-dev.json';
      }
    return  new HttpClient(this.handler).get(this.dataLoc)
      .toPromise()
      .then((data:AppConfig) => {
        this.logger.info(data);
        AppConfigService.appConfig = data;
      });
  }
  getConfig() {
    return AppConfigService.appConfig;
  }
}
Run Code Online (Sandbox Code Playgroud)

感谢@ysf,您的最小示例让我知道,如果它一般正常工作,那么在初始化期间还有其他东西正在调用 authconfig 。