2-4 配置文件的参数验证Joi方案
配置验证是指在应用启动时,如果没有提供必需的环境变量或不符合验证规则,就抛出异常,防止应用在错误配置下运行。
@nestjs/config 提供两种验证方式:
- Joi 内置验证器:通过定义对象 Schema 来校验 JavaScript 对象
- 自定义
validate()函数:将环境变量作为输入进行校验
Joi 方案
安装依赖:
npm install joi
bash
注意:Joi 最新版本需要 Node v12+。如果遇到构建错误,请安装
joi@17.1.8以前的版本。
在 app.module.ts 中定义验证 Schema:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';
const envPath = `.env.${process.env.NODE_ENV || 'development'}`;
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: envPath,
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production', 'test', 'provision')
.default('development'),
PORT: Joi.number().default(3000),
DB_HOST: Joi.string().ip(),
DB_URL: Joi.string().domain(),
DATABASE_USER: Joi.string().required(),
}),
}),
],
})
export class AppModule {}
typescript
常用 Joi 校验规则
| 规则 | 说明 |
|---|---|
Joi.string() | 字符串类型 |
Joi.number() | 数字类型 |
Joi.string().required() | 必填 |
Joi.string().default(val) | 默认值 |
Joi.string().valid('a', 'b') | 枚举范围 |
Joi.number().default(3306) | 数字默认值 |
Joi.string().ip() | IP 地址格式 |
Joi.string().domain() | 域名格式 |
Joi.string().email() | 邮箱格式 |
Joi.string().pattern(regex) | 正则校验 |
Joi.string().custom(fn) | 自定义校验 |
更多校验规则参考 Joi API 文档。
验证效果
配置错误的脚本(PORT 为非数字):
"start:dev": "cross-env NODE_ENV=development PORT=toimc nest start --watch"
json
启动时会报错:
Error: Config validation error: "PORT" must be a number
text
当 DATABASE_USER 为空时:
Error: Config validation error: "DATABASE_USER" is not allowed to be empty
text
validationOptions 配置
ConfigModule.forRoot({
validationSchema: Joi.object({ ... }),
validationOptions: {
allowUnknown: false, // 是否允许未知键,默认 true
abortEarly: true, // 遇到第一个错误就停止,默认 false
},
})
typescript
注意事项
Joi 主要校验两个来源的参数:
process.env传入的命令行参数envFilePath加载的.env文件中的参数
如果使用 load 方法加载公共 .env 文件,Joi 不会校验 load 加载的内容(这是已知的问题)。解决方案是将 envFilePath 改为数组形式,把公共 .env 文件放在后面:
ConfigModule.forRoot({
envFilePath: [envPath, '.env'],
// 不再使用 load 方法
validationSchema: Joi.object({ ... }),
})
typescript
class-validator 替代方案
如果不想引入 Joi,可以使用 class-validator + class-transformer 实现等价的校验效果。
安装依赖:
npm install class-validator class-transformer
bash
定义校验文件 src/env.validation.ts:
import { plainToClass } from 'class-transformer';
import { IsEnum, IsNumber, validateSync } from 'class-validator';
enum Environment {
Development = 'development',
Production = 'production',
}
class EnvironmentVariables {
@IsEnum(Environment)
NODE_ENV: Environment;
@IsNumber()
PORT: number;
}
export function validate(config: Record<string, unknown>) {
const validatedConfig = plainToClass(EnvironmentVariables, config, {
enableImplicitConversion: true,
});
const errors = validateSync(validatedConfig, { skipMissingProperties: false });
if (errors.length > 0) {
throw new Error(errors.toString());
}
return validatedConfig;
}
typescript
在 app.module.ts 中使用:
import { validate } from './env.validation';
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: envPath,
validate,
}),
],
})
export class AppModule {}
typescript
小结
- 使用第三方
config库可以方便读取配置信息,但校验需要在读取位置手动添加 - 官方
@nestjs/config可以方便导入.env文件,结合js-yaml也能导入 YAML 格式配置 - 推荐方案:
@nestjs/config+Joi进行参数校验 class-validator只是冰山一角,后续数据验证中还会深入使用
↑