"Cannot read property 'displayCall' of undefined at new AuthenticationContex ..."

"Cannot read property 'displayCall' of undefined at new AuthenticationContex ..."

最近怎么样?

所以,我刚开始使用 TypeScript 使用最新版本的 Angular,但在尝试将配置文件加载到我的应用程序时遇到了问题。

事情大致是这样的:我正在开发一个项目,该项目使用 MsAdalAngular6Module for Azure验证。当我得到项目时,所有配置设置都是硬编码的,所以我按照以下说明进行操作:https://devblogs.microsoft.com/premier-developer/angular-how-to-microsoft-adal-for-angular-6-with-configurable-settings/ to use the package in a more realistic scenario. But then, as you gonna find in the first link, I had to create a service to load my configuration files dynamically following those instructions: https://devblogs.microsoft.com/premier-developer/angular-how-to-editable-config-files/

之后一切顺利,我能够加载我的配置文件,没问题。但是当我需要创建一个 HttpInterceptor 以在每个请求的 header 中插入身份验证令牌时,问题就开始了。基本上,我不能同时使用两者(加载配置文件的服务和 HttpInterceptor),否则我会收到此错误:

Error when trying to load configuration files

我的猜测是:来自配置服务的请求是在 MsAdalAngular6Module 加载之前发出的。因此,当发出请求时,它没有一些属性,它应该具有 ir order 以便它被 MsAdalAngular6Service 进行身份验证。当然,这只是一个猜测。

这是一些代码:

配置服务


    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { environment } from '../environments/environment';
    import { IAppConfig } from './models/app-config.model';

    @Injectable()
    export class AppConfig {
        static settings: IAppConfig;
        constructor(private http: HttpClient) { }
        load() {
            const jsonFile = `assets/config/config.${environment.name}.json`;
            return new Promise<void>((resolve, reject) => {
                this.http.get(jsonFile, { withCredentials: true }).toPromise().then((response: IAppConfig) => {
                    AppConfig.settings = <IAppConfig>response;
                    resolve();
                }).catch((response: any) => {
                    reject(`Could not load file '${jsonFile}': ${JSON.stringify(response)}`);
                });
            });
        }
    }

Http 拦截器


    import { Injectable } from '@angular/core';
    import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
    import { mergeMap } from 'rxjs/operators';
    import { MsAdalAngular6Service } from 'microsoft-adal-angular6';

    @Injectable()
    export class InsertAuthTokenInterceptor implements HttpInterceptor {

        constructor(private adal: MsAdalAngular6Service) { }

        intercept(req: HttpRequest<any>, next: HttpHandler) {
            // get api url from adal config
            const resource = this.adal.GetResourceForEndpoint(req.url);
            if (req.withCredentials){
                return next.handle(req);
            }

            if (!resource || !this.adal.isAuthenticated) {
                return next.handle(req);
            }

            // merge the bearer token into the existing headers
            return this.adal.acquireToken(resource).pipe(
                mergeMap((token: string) => {
                    const authorizedRequest = req.clone({
                        headers: req.headers.set('Authorization', `Bearer ${token}`),
                    });
                    return next.handle(authorizedRequest);
            }));
        }
    }

以及我在app.module.ts上使用它的方式:

    ...
    let adalConfig: any;

    export const interceptorProviders =
      [
        {provide: HTTP_INTERCEPTORS, useClass: InsertAuthTokenInterceptor, multi: true}
      ];



    export function msAdalAngular6ConfigFactory() {
      return adalConfig;
    }

    export function initializeApp(appConfig: AppConfig) {
      const promise = appConfig.load().then(() => {
        adalConfig = {
          tenant: AppConfig.settings.adalConfig.tenant,
          clientId: AppConfig.settings.adalConfig.clientId,
          endpoints: AppConfig.settings.adalConfig.endpoints,
          navigateToLoginRequestUrl: false,
          cacheLocation: AppConfig.settings.adalConfig.cacheLocation
        };
      });

      return () => promise;
    }

    ...

提供商部分

    ...
      providers: [
        AppConfig,
        {
          provide: APP_INITIALIZER,
          useFactory: initializeApp,
          deps: [AppConfig],
          multi: true,
        },
        interceptorProviders,
        AuthenticationGuard,
        LoaderService,
        MsAdalAngular6Service,
        {
          provide: 'adalConfig',
          useFactory: msAdalAngular6ConfigFactory,
          deps: []
        },
        AuthenticationGuard
      ]
    ...

好的,我想我找到了解决办法。只是不知道可信度如何。我使用了一个本地的 htpp 客户端。然后,AppConfig 服务变成了这样:

    import { Injectable } from '@angular/core';
    import { HttpBackend, HttpClient } from '@angular/common/http';
    import { environment } from '../environments/environment';
    import { IAppConfig } from './models/app-config.model';

    const URL = `assets/config/config.${environment.name}.json`;

    @Injectable({ providedIn: 'root' }) export class AppConfig {
        static settings: IAppConfig;

        private httpClient: HttpClient; constructor(httpBackend: HttpBackend) {
            // Use local HttpClient to avoid interceptor loop
            this.httpClient = new HttpClient(httpBackend);
        }

        getAppConfig() {
            return new Promise<void>((resolve, reject) => {
                this.httpClient.get(URL, { withCredentials: true }).toPromise().then((response: IAppConfig) => {
                    AppConfig.settings = <IAppConfig>response;
                    resolve();
                }).catch((response: any) => {
                    reject(`Could not load file '${URL}': ${JSON.stringify(response)}`);
                });
            });
        }
    }