在Angular App中配置模块的三种方法

208 阅读10分钟

配置是开发者生活的一部分。配置数据是你的应用程序运行所需的信息,可能包括第三方系统的令牌或你传递给库的设置。在Angular中,有不同的方式来加载配置数据作为应用程序初始化的一部分。你对配置数据的要求可能会根据需求而改变。例如,你的应用程序可能有一个不变的配置,或者你可能需要基于应用程序运行环境的不同配置。我们将介绍几种不同的方法来加载配置值,并确定何时应该使用每种方法。

我们将介绍使用Angular的具体用例,所以这篇文章假定你有一些开发Angular应用的经验。如果你有兴趣了解更多关于建立你的第一个Angular应用程序的信息,请查看Angular入门文档或指导你将Okta集成到Angular应用程序的教程链接

在这篇文章中,我们将介绍以下几种形式的配置:

  • 直接在代码中定义配置
  • 为不同的环境定义配置
  • 通过API调用加载配置数据

我们将展示每种方法的例子,包括如何与Okta集成。此外,我们还将确定何时使用每种技术以及需要注意的事项。

目录

在一个示例项目中设置Angular和Okta

首先,我们将设置基本项目和Okta资源,这样你就可以跟着帖子走了。

为了保持公平竞争,避免任何新的Angular功能的滑稽行为,我将在代码样本中使用Angular v9应用程序。所有概述的方法都适用于从Angular v7到目前的版本,即Angular v13。

创建Angular应用程序

你需要一个适用于你要创建的Angular应用版本的Nodenpm

我使用Node v14.18.1和npm v6.14.15来创建Angular v9应用程序,但你可以为你喜欢的Angular v7以上版本创建应用程序。

使用你在全球范围内安装的Angular CLI来创建一个带有路由和标准CSS的Angular应用,通过运行来进行样式设计。

ng new async-load --routing --style=css

或者通过运行以下命令来创建Angular v9应用程序。

npx @angular/cli@9 new async-load --routing --style=css

创建Okta应用程序

让我们创建Okta资源,以拥有我们需要整合的配置值。

在你开始之前,你需要一个免费的Okta开发者账户。安装Okta CLI并运行okta register ,注册一个新账户。如果你已经有一个账户,运行okta login 。然后,运行okta apps create 。选择默认的应用程序名称,或根据你的需要进行更改。 选择单页应用程序,然后按回车键

在重定向URI中使用http://localhost:4200/login/callback ,并将注销重定向URI设置为http://localhost:4200

Okta CLI是做什么的?

Okta CLI将在您的Okta组织中创建一个OIDC单页应用。它将添加您指定的重定向URI并授予Everyone组的访问权限。它还会为http://localhost:4200 添加一个受信任的来源。当它完成时,你会看到如下的输出。

Okta application configuration:
Issuer:    https://dev-133337.okta.com/oauth2/default
Client ID: 0oab8eb55Kb9jdMIr5d6

注意:你也可以使用Okta管理控制台来创建你的应用程序。更多信息请参见创建一个Angular应用程序

我们将需要Okta Angular和Okta Auth JS库。通过运行以下命令将它们添加到您的应用程序中。

npm install @okta/okta-angular@5.1 @okta/okta-auth-js@6.0

这篇文章不会指导你设置签入和签出;我们只对设置配置感兴趣。如果Angular应用程序运行时没有错误,说明配置方面是正确的。要看到我们试图避免的错误类型,尝试排除issuer ,或者不要用你从Okta CLI得到的值替换{yourOktaDomain} 。示例代码 repo确实集成了签入和签出,所以你可以看到认证工作的全部过程。

在代码中定义配置

当你的配置是静态的,配置库的最直接方法是直接在代码中定义配置。在这种方法中,你会在AppModule ,或者在这个方法的功能模块中定义配置数据。这种方法的例子可能看起来像定义路由的配置并将其传入RouterModule

const routes: Routes = [
  { path: 'profile', component: ProfileComponent }
];

@NgModule({
  declarations: [ AppComponent, ProfileComponent ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes)
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

你可能会惊讶地看到路由作为直接在代码中定义配置的一个例子。 然而,当你把应用范围内的配置传入模块的forRoot() 静态方法时,这正是你在做的事情。

如果你跟随我们的许多代码例子和博客文章,将Okta集成到Angular应用程序中,你会遵循类似的模式,即配置直接在应用程序中定义。

你的配置代码看起来是这样的。

import { OktaAuthModule, OKTA_CONFIG } from '@okta/okta-angular';
import { OktaAuth } from '@okta/okta-auth-js';

const oktaAuth = new OktaAuth({
  issuer: '/oauth2/default',
  clientId: '{yourClientId', 
  redirectUri: window.location.origin + '/login/callback'
});

@NgModule({
  declarations: [ AppComponent, ProfileComponent ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    OktaAuthModule
  ],
  providers: [
    { provide: OKTA_CONFIG, useValue: { oktaAuth } }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

在代码中定义配置的总结。

当配置不因外部因素而改变时,这是向你的应用程序添加配置的最直接的方式。

什么时候使用。

  • 尽可能多地使用,因为这是最简单的配置方式。

最佳用例。

  • 静态的应用配置
  • 配置第三方库
  • 快速测试

要注意的是。

  • 涉及私钥或令牌的配置

因环境而改变的配置

Angular有一个内置的方法来支持每个环境的差异,使用environments/environment.*.ts 文件。当本地服务时,Angular CLI使用environments/environment.ts 中的值,而当你为生产构建时,Angular CLI会替换成environment.prod.ts 。你可以在angular.json 构建配置中看到这种文件替换的定义。如果你有更多的环境需要支持,你可以自定义构建配置以满足你的需求。

当你有不同的配置想在构建时支持时,环境文件是有帮助的。一些例子包括只在prod环境中启用用户分析,或定义QA环境调用的API端点。

src/main.ts 包含一个根据环境改变配置的例子。在这里你可以看到以下内容。

import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

Angular利用环境文件来确定何时调用enableProdMode() 方法。注意从./environments/environment 的文件导入。这是因为构建过程处理了该文件的交换。

现在让我们看看在与Okta集成时我们如何使用这个。

src/environments/environment.ts ,像这样添加Okta auth配置。

export const environment = {
  production: false,
  authConfig: {
    issuer: '/oauth2/default',
    clientId: '{yourClientId}'
  }
};

src/environments/environment.prod.ts ,你将添加同样的authConfig 属性,其值与你的prod环境一致。

你将使用环境文件来初始化AppModule 中的OktaAuthModule ,像这样。

import { OktaAuthModule, OKTA_CONFIG } from '@okta/okta-angular';
import { OktaAuth } from '@okta/okta-auth-js';
import { environment } from '../environments/environment.ts';

const oktaAuth = new OktaAuth({
  ...environment.authConfig,
  redirectUri: window.location.orgin + '/login/callback'
});

@NgModule({
  declarations: [ AppComponent, ProfileComponent ],
  imports: [ BrowserModule, AppRoutingModule, OktaAuthModule ],
  providers: [
    { provide: OKTA_CONFIG, useValue: { oktaAuth }}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

按环境变化的配置总结。

定制环境文件是Angular推荐的方法,可以在构建时注入数值。

何时使用。

  • 你有基于构建输出的不同配置值

最佳用例。

  • Devmode--让本地提供服务的应用不做只有prod应用才应该做的事情
  • 多个暂存环境系统

要注意的是。

  • 涉及私钥或令牌的配置
  • 为每个环境运行构建,以测试你所做的改变。你不想错过添加一个属性,并有可能得到一个运行时错误。

从外部API加载配置

有时你需要在运行时加载配置。如果你使用发布推广式的部署,这就很有意义了--为暂存/预生产环境创建一个构建,并在验证后将同一构建推广到生产环境。你不想创建一个新的构建,但如果你的暂存环境和生产环境需要不同的配置怎么办?在这样的情况下,从外部API加载配置是很方便的。

为了使这种外部API配置方法保持简单,我将只关注Okta的例子。

在这个例子中,我们将看看src/main.ts ,在那里我们启动Angular应用程序。当你需要在应用加载前进行配置时,我们可以利用platformBrowserDynamic() 平台注入器的extraProviders 功能。extraProviders 允许我们提供平台提供者,这与我们在AppModule 中提供应用程序范围的提供者的方式相同。

由于我们需要在拥有完整的Angular应用程序上下文之前进行服务器调用以获得配置,所以我们使用Web APIs来调用API。然后我们可以为Okta的OKTA_CONFIG 注入令牌配置提供者。

对于一个配置API调用响应,看起来像这样。

{
  "issuer": "/oauth2/default",
  "clientId": "{yourClientId}", 
  "redirectUri": "{correctHostForTheEnvironment}/login/callback"
}

...你的src/main.ts 中的代码改为

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { OKTA_CONFIG } from '@okta/okta-angular';
import { OktaAuth } from '@okta/okta-auth-js';

if (environment.production) {
  enableProdMode();
}

fetch('http://{yourApiUri}/config').then(async res => {
  const authConfig = await res.json();

  platformBrowserDynamic([
    { provide: OKTA_CONFIG, useValue: {oktaAuth: new OktaAuth(authConfig)}}
  ]).bootstrapModule(AppModule)
    .catch(err => console.error(err));
});

然后你的AppModule 只需要导入OktaAuthModule ,因为你已经提供了OKTA_CONFIG 的注入令牌。

如果你需要以编程方式创建回调URI,或者你需要在多个地方使用配置,你可以把配置存储在应用程序中。我们最少需要的是一个保存配置的类,我们将在例子中展示这个类。如果你的需求比我们在这里展示的更多,你可以把配置包裹在一个服务中。

你将添加一个新文件,并创建一个与响应相匹配的接口,以及一个容纳配置的类。

export interface AuthConfig {
  issuer: string;
  clientId: string;
}

export class OktaAuthConfig {
  constructor(public config: AuthConfig) { }
}

编辑src/main.ts ,以代替提供OktaAuthConfig 类。

import { OktaAuthConfig } from './app/auth-config';

fetch('http://{yourApiUri}/config').then(async res => {
  const authConfig = new OktaAuthConfig(await res.json());

  platformBrowserDynamic([
    { provide: OktaAuthConfig, useValue: authConfig }
  ]).bootstrapModule(AppModule)
  .catch(err => console.error(err));
})

AppModule ,你可以通过访问OktaAuthConfig ,提供与Okta集成所需的OKTA_CONFIG

@NgModule({
  declarations: [ AppComponent, ProfileComponent ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    OktaAuthModule
  ],
  providers: [
    {
      provide: OKTA_CONFIG,
      deps: [OktaAuthConfig],
      useFactory: (oktaConfig: OktaAuthConfig) => ({
        oktaAuth: new OktaAuth({
          ...oktaConfig.config,
          redirectUri: window.location.origin + '/login/callback'
        })
      })
    }
  ]
})
export class AppModule { }

你现在可以从API加载一个配置,并使用应用程序的位置。

你可能会问自己,"不是有一个APP_INITIALIZER token或其他东西可以代替我们使用吗"?嗯,是的,有一个APP_INITIALIZER token,用于执行在应用初始化完成之前完成的初始化功能。然而,在我们的例子中,我们需要auth配置才能初始化。所以,我们需要在初始化应用之前完成配置的加载,这一点我们可以在启动时进行。

从外部API加载配置的总结

从API加载配置,并将配置提供给应用程序。根据你的需要,配置加载可以在启动过程中进行,也可以通过APP_INITIALIZER

何时使用

  • 你希望配置在运行时加载,而不是被烘烤到代码中去
  • 你的配置属性包括你不想提交给源代码控制的私人信息

最佳用例

  • 你有不同的配置用于staging和prod,并使用release-promotion风格的部署流程
  • 你的配置经常改变,或者经常改变,以至于构建和部署应用程序不可行。

注意以下情况

  • 配置错误或网络突发事件--你的应用程序将无法运行,因为它依赖于外部API。
  • 任何可能降低应用加载速度的情况,如过大的配置响应、调用过多的端点或缓慢的服务器响应。
  • 验证和测试方面的潜在挑战,因为配置可能会改变。

了解更多关于Angular的信息

我希望这篇文章对你考虑如何将Okta整合到你的Angular应用中有所帮助。