【问题标题】:How do I externalize property values for an Angular 7 application?如何外部化 Angular 7 应用程序的属性值?
【发布时间】:2019-07-17 23:29:22
【问题描述】:

我有一个 Angular 7 应用程序,其中 app.module.ts 文件如下所示。请注意,在此示例中,有 2 个模块,每个模块都有 1 个键,其值需要外部化。 externalized 的意思是应该在运行时从环境中获取值。

@NgModule({
 declarations: [ ... ],
 imports: [
  SomeModule.forRoot({ apiKey1: "needs to be externalized" }),
  AnotherModule.forRoot({ apiKey2: "needs to also be externalized" })
 ],
 providers: [ ... ],
 bootstrap: [AppComponent]
})
export class AppModule { }

我要做的是构建这个应用程序(例如ng build,然后使用 Docker 将其容器化)。在部署时,DevOps 人希望按如下方式运行 docker 容器。

docker run -e API_KEY_1='somekey' -e API_KEY_2='anotherkey' -p 80:80 my-container:production

请注意,API_KEY_1 应映射到 apiKey1API_KEY_2 应映射到 apiKey2

是否有任何规范的方法可以将 Angular 应用程序的值外部化?

我曾想过编写一个帮助脚本来对文件进行字符串替换,但我认为这种方法不是很规范(因为转译的 Angular 应用程序文件被混淆和缩小了)。该脚本将在容器启动时运行,读取环境变量(键和值),然后查看文件以将旧值替换为环境中的值。

最终,Angular 应用程序将与 Kubernetes 进行编排。我想知道是否有任何东西可以帮助或影响如何以最佳实践方式将价值观外化。

感谢任何帮助。

【问题讨论】:

  • 容器启动时创建一个credentials.json怎么样?您可以将每个键值对作为 json、assets/ 路径或其他地方。然后 Angular 应用程序可以通过 AJAX 调用(GET /assets/credentials.json)获取它。
  • 哦,我的回答可能很危险,因为用户可以在 Chrome 检查器的网络选项卡中获取值。如果您可以加密 json 并在运行时对其进行解密,则应用程序可以安全地获取 api 密钥。
  • @kuroneko0441 问题是 Angular 模块显示了代码示例,其中键放置在 app.module.ts 中。这些东西被推送到js 文件,如果有人真的想要,他们可以重新格式化和研究js 文件并抓取它们(或者如你所说,观察流量)。我认为您的担忧是有效的(加密/解密)且相关;但这篇文章是关于将这些值外部化,以便无论在何处部署应用程序,部署过程都可以通过环境变量注入它们。

标签: angular docker kubernetes properties-file


【解决方案1】:

您可以在自定义入口点中使用替换。

FROM nginx

RUN apt-get update && apt-get -y install gettext-base nginx-extras

ADD docker-entrypoint.sh /
ADD settings.json.template /

COPY dist /usr/share/nginx/html

ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

像这样使用docker-entrypoint.sh

#!/bin/bash

envsubst < "settings.json.template" > "settings.json"
cp settings.json /usr/share/nginx/html/assets/

# Launch nginx
exec "$@"

还有一个settings.json.template

{
  "apiKey2": "$API_KEY_2",
  "apiKey1": "$API_KEY_1"
}

然后在您的源上添加一个文件 settings-loader.ts

export const settingsLoader = new Promise<any>((resolve, reject) => {
  const xmlhttp = new XMLHttpRequest();
  const method = 'GET';
  const url = './assets/settings.json';

  xmlhttp.open(method, url, true);

  xmlhttp.onload = function() {
    if (xmlhttp.status === 200) {
      const _environment = JSON.parse(xmlhttp.responseText);
      resolve(_environment);
    } else {
      resolve();
    }
  };

  xmlhttp.send();
});

在你的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 {settingsLoader} from 'settings-loader';

settingsLoader.then((settings) => {
  if (settings != null) {
    environment.settings = Object.assign(environment.settings, settings);
  }

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

  platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
});

那么您应该可以使用

访问您的代码
import {environment} from '../environments/environment';

console.log(environment.settings.apiKey1);

【讨论】:

  • 这种方法适用于依赖注入后的服务/组件,但它是否适用于 @NgModule 级别的依赖注入期间或之前?如果我们已经在做envsubt,我想说的是,一般来说,在启动时,只需对所有生成的文件进行一次替换(不需要上面的settingsLoader 的额外代码)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-10
  • 2022-01-22
  • 2020-10-15
  • 2016-10-07
  • 2020-11-14
  • 2018-09-09
  • 2015-04-03
相关资源
最近更新 更多