首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

组态

用于在不同环境中运行的应用程序。根据环境,应使用各种配置变量集。例如,本地环境很可能在特定数据库凭证上进行中继,仅对本地数据库实例有效。为了解决这个问题,我们过去常常利用.env保存键值对的文件,其中每个键代表一个特定的值,因为这种方法非常方便。

但是当我们使用process全局对象时,由于测试类可能直接使用它,因此很难保持测试清洁。另一种方法是创建一个抽象层,ConfigModule它暴露出一个ConfigService带有加载的配置变量。

安装

某些平台会自动将我们的环境变量附加到process.env全局。但是,在当地环境中,我们必须手动处理它。为了解析我们的环境文件,我们将使用dotenv包。

代码语言:javascript
复制
$ npm i --save dotenv

服务

首先,让我们创建一个ConfigService类。

JS

代码语言:javascript
复制
import * as dotenv from 'dotenv';
import * as fs from 'fs';

export class ConfigService {
  private readonly envConfig: { [key: string]: string };

  constructor(filePath: string) {
    this.envConfig = dotenv.parse(fs.readFileSync(filePath))
  }

  get(key: string): string {
    return this.envConfig[key];
  }
}

该类采用单个参数a filePath,它是.env文件的路径。get()提供该方法以允许访问envConfig保存环境文件内定义的每个属性的私有对象。

最后一步是创建一个ConfigModule

JS

代码语言:javascript
复制
import { Module } from '@nestjs/common';
import { ConfigService } from './config.service';

@Module({
  providers: [
    {
      provide: ConfigService,
      useValue: new ConfigService(`${process.env.NODE_ENV}.env`),
    },
  ],
  exports: [ConfigService],
})
export class ConfigModule {}

ConfigModule寄存器中ConfigService,出口它。另外,我们传递了一个.env文件路径。根据实际执行环境,此路径将有所不同。现在,您可以简单地ConfigService在任何地方注入,并根据传递的密钥提取特定值。示例.env文件如下所示:

development.env

代码语言:javascript
复制
DATABASE_USER=test
DATABASE_PASSWORD=test

使用ConfigService

要从我们访问环境变量ConfigService我们需要注入它。因此我们首先需要导入模块。

app.module.ts

代码语言:javascript
复制
@Module({
  imports: [ConfigModule],
  ...
})

之后,您可以使用注入令牌注入它。默认情况下,令牌等于类名(在我们的示例中ConfigService)。

app.service.ts

代码语言:javascript
复制
@Injectable()
export class AppService {
  private isAuthEnabled: boolean;
  constructor(config: ConfigService) {
    // Please take note that this check is case sensitive!
    this.isAuthEnabled = config.get('IS_AUTH_ENABLED') === 'true' ? true : false;
  }
}

ConfigModule您也可以将其声明ConfigModule为全局模块,而不是在所有模块中重复导入。

高级配置

我们刚刚实施了一个基础ConfigService。但是,这种方法有一些缺点,我们现在将解决:

  • 缺少环境变量的名称和类型(没有IntelliSense)
  • 缺乏对所提供文件的验证.env
  • env文件将booleans作为string('true')提供,因此必须boolean每次都将它们强制转换为

验证

我们将从提供的环境变量的验证开始。如果未提供所需的环境变量或者它们不符合您的预定义要求,则可能会抛出错误。为此,我们将使用npm包Joi。使用Joi,您可以定义一个对象模式并根据它来验证JavaScript对象。

安装Joi及其类型(适用于TypeScript用户):

代码语言:javascript
复制
$ npm install --save joi
$ npm install --save-dev @types/joi

一旦安装了包,我们就可以转移到我们的ConfigService

config.service.ts

代码语言:javascript
复制
import * as Joi from 'joi';
import * as fs from 'fs';

export interface EnvConfig {
  [key: string]: string;
}

export class ConfigService {
  private readonly envConfig: EnvConfig;

  constructor(filePath: string) {
    const config = dotenv.parse(fs.readFileSync(filePath));
    this.envConfig = this.validateInput(config);
  }

  /**
   * Ensures all needed variables are set, and returns the validated JavaScript object
   * including the applied default values.
   */
  private validateInput(envConfig: EnvConfig): EnvConfig {
    const envVarsSchema: Joi.ObjectSchema = Joi.object({
      NODE_ENV: Joi.string()
        .valid(['development', 'production', 'test', 'provision'])
        .default('development'),
      PORT: Joi.number().default(3000),
      API_AUTH_ENABLED: Joi.boolean().required(),
    });

    const { error, value: validatedEnvConfig } = Joi.validate(
      envConfig,
      envVarsSchema,
    );
    if (error) {
      throw new Error(`Config validation error: ${error.message}`);
    }
    return validatedEnvConfig;
  }
}

由于我们的设置默认值NODE_ENVPORT如果我们不提供环境文件这些变量验证也不会失败。不过,我们需要明确提供API_AUTH_ENABLED。如果我们的.env文件中的变量不是模式的一部分,验证也会抛出错误。此外,Joi尝试将env字符串转换为正确的类型。

类属性

对于每个配置属性,我们必须添加一个getter函数。

config.service.ts

代码语言:javascript
复制
get isApiAuthEnabled(): boolean {
  return Boolean(this.envConfig.API_AUTH_ENABLED);
}

用法示例

现在我们可以直接访问类属性。

app.service.ts

代码语言:javascript
复制
@Injectable()
export class AppService {
  constructor(config: ConfigService) {
    if (config.isApiAuthEnabled) {
      // Authorization is enabled
    }
  }
}

扫码关注腾讯云开发者

领取腾讯云代金券