koatty_validation
基于 class-validator 扩展的 Koatty 验证工具库,支持中文本地化验证规则、自定义装饰器、性能缓存和错误处理。

✨ 特性
- 🚀 高性能: 内置缓存机制,提升验证性能
- 🌏 中文支持: 内置中文验证规则(姓名、身份证、手机号等)
- 🔧 自定义装饰器: 支持装饰器工厂模式,轻松创建自定义验证器
- 📊 性能监控: 内置性能监控和缓存统计
- 🎯 错误处理: 多语言错误信息支持
- 📦 TypeScript: 完整的 TypeScript 支持
📦 安装
npm install koatty_validation
yarn add koatty_validation
🎯 快速开始
基础用法
import { IsNotEmpty, IsCnName, IsMobile, Valid, Validated } from 'koatty_validation';
export class Controller {
Test(@Valid("IsNotEmpty", "不能为空") id: number) {
}
@Validated()
TestDto(user: UserDTO) {
}
@Validated(false)
TestDtoSync(user: UserDTO) {
}
}
export class UserDTO {
@IsNotEmpty({ message: "手机号不能为空" })
@IsMobile({ message: "手机号格式不正确" })
phoneNum: string;
@IsCnName({ message: "姓名必须是有效的中文姓名" })
userName: string;
}
📋 可用装饰器
🇨🇳 中文验证装饰器
@IsCnName()
@IsIdNumber()
@IsMobile()
@IsZipCode()
@IsPlateNumber()
🌐 通用验证装饰器
@IsNotEmpty()
@IsEmail()
@IsIP()
@IsPhoneNumber()
@IsUrl()
@IsHash()
@IsDate()
🔢 数值比较装饰器
@Gt(10)
@Gte(10)
@Lt(100)
@Lte(100)
@Equals('value')
@NotEquals('x')
📝 字符串验证装饰器
@Contains('test')
@IsIn(['a', 'b', 'c'])
@IsNotIn(['x', 'y', 'z'])
🛠️ 控制装饰器
@Valid(rule, options)
@Validated()
@Validated(true)
@Validated(false)
@Expose()
@IsDefined()
🎭 Validated 装饰器
@Validated 装饰器支持同步和异步两种验证模式,以适应不同的应用场景。
异步模式(默认)
适用于 Koatty 框架中,控制器方法的参数需要异步获取的场景。
import { Validated, checkValidated } from 'koatty_validation';
class UserController {
@Validated()
async register(user: UserDTO) {
return { success: true };
}
@Validated(true)
async update(id: number, user: UserDTO) {
return { success: true };
}
}
异步模式特点:
- ✅ 装饰器保存验证元数据到 IOC 容器
- ✅ 由框架在异步获取参数后执行验证
- ✅ 适用于参数值需要异步获取的场景
- ✅ 是 Koatty 框架的推荐模式
同步模式
适用于单元测试或参数值已经准备好的场景。
class UserService {
@Validated(false)
async createUser(user: UserDTO) {
return { success: true };
}
@Validated(false)
async updateUser(id: number, user: UserDTO) {
return { success: true };
}
}
同步模式特点:
- ✅ 装饰器包装原方法,在调用时立即执行验证
- ✅ 适用于单元测试场景
- ✅ 适用于参数已准备好的场景
- ✅ 验证失败立即抛出错误
手动调用 checkValidated
在框架拦截器或中间件中,可以手动调用 checkValidated 函数:
import { checkValidated } from 'koatty_validation';
async function validateInMiddleware(args: any[], paramTypes: any[]) {
try {
const { validatedArgs, validationTargets } = await checkValidated(args, paramTypes);
console.log('验证通过');
return validationTargets;
} catch (error) {
console.error('验证失败:', error);
throw error;
}
}
选择合适的模式
| Koatty 框架控制器 | 异步 @Validated() | 参数需要异步获取 |
| 单元测试 | 同步 @Validated(false) | 参数已准备好,立即验证 |
| 独立服务/工具 | 同步 @Validated(false) | 不依赖框架,立即验证 |
| 框架拦截器 | 手动 checkValidated() | 完全控制验证时机 |
🔧 自定义装饰器
使用装饰器工厂创建自定义验证器
import { createSimpleDecorator, createParameterizedDecorator } from 'koatty_validation';
export const IsPositiveInteger = createSimpleDecorator(
'IsPositiveInteger',
(value: any) => {
const num = Number(value);
return Number.isInteger(num) && num > 0;
},
'must be a positive integer'
);
export const InRange = createParameterizedDecorator(
'InRange',
(value: any, min: number, max: number) => {
const num = Number(value);
return num >= min && num <= max;
},
'must be between $constraint1 and $constraint2'
);
class ProductDto {
@IsPositiveInteger()
quantity: number;
@InRange(0, 100)
discountPercent: number;
}
高级自定义装饰器
import { createValidationDecorator } from 'koatty_validation';
export function IsStrongPassword(validationOptions?: ValidationOptions) {
return createValidationDecorator({
name: 'IsStrongPassword',
validator: (value: string) => {
const hasLowercase = /[a-z]/.test(value);
const hasUppercase = /[A-Z]/.test(value);
const hasNumbers = /\d/.test(value);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value);
return value.length >= 8 && hasLowercase && hasUppercase && hasNumbers && hasSpecialChar;
},
defaultMessage: 'password must be at least 8 characters with uppercase, lowercase, number and special character',
requiresValue: false
})(validationOptions);
}
🚀 性能优化
缓存预热
import { warmupCaches, performanceMonitor } from 'koatty_validation';
await warmupCaches();
const timer = performanceMonitor.startTimer('validation');
timer();
const report = performanceMonitor.getReport();
console.log(report);
缓存统计
import { getAllCacheStats, clearAllCaches } from 'koatty_validation';
const stats = getAllCacheStats();
console.log(stats);
clearAllCaches();
🌐 错误处理
多语言支持
import { setValidationLanguage, KoattyValidationError } from 'koatty_validation';
setValidationLanguage('zh');
try {
await validate(userDto);
} catch (error) {
if (error instanceof KoattyValidationError) {
console.log('验证错误:', error.message);
console.log('错误详情:', error.errors);
}
}
错误格式化
import { errorFormatter } from 'koatty_validation';
const errors = await validate(dto);
if (errors.length > 0) {
const formatted = errorFormatter(errors, 'zh');
console.log(formatted);
}
📖 手动验证
FunctionValidator
import { FunctionValidator } from 'koatty_validation';
try {
FunctionValidator.IsNotEmpty("", "不能为空");
FunctionValidator.IsMobile("123", "手机号格式不正确");
} catch (error) {
console.log(error.message);
}
FunctionValidator.Contains(str, {
message: "必须包含字母s",
value: "s"
});
ValidFuncs (纯函数)
import { ValidFuncs } from 'koatty_validation';
if (!ValidFuncs.IsNotEmpty(str)) {
console.log("字符串为空");
}
if (!ValidFuncs.IsCnName("张三")) {
console.log("不是有效的中文姓名");
}
if (!ValidFuncs.IsMobile("13812345678")) {
console.log("不是有效的手机号");
}
ClassValidator
import { ClassValidator } from 'koatty_validation';
class UserSchema {
@IsDefined()
id: number;
@IsNotEmpty()
name: string;
@IsMobile()
phone: string;
}
try {
const result = await ClassValidator.valid(UserSchema, {
id: 1,
name: '',
phone: '123'
});
} catch (error) {
console.log('验证失败:', error.message);
}
const validatedData = await ClassValidator.valid(UserSchema, rawData, true);
📚 更多示例
查看 examples 目录获取更多使用示例:
🔍 可用验证函数
所有验证函数都同时提供装饰器和函数两种形式:
| IsCnName | 中文姓名 | ValidFuncs.IsCnName("张三") |
| IsIdNumber | 身份证号 | ValidFuncs.IsIdNumber("110101199001011234") |
| IsMobile | 手机号 | ValidFuncs.IsMobile("13812345678") |
| IsZipCode | 邮政编码 | ValidFuncs.IsZipCode("100000") |
| IsPlateNumber | 车牌号 | ValidFuncs.IsPlateNumber("京A12345") |
| IsEmail | 邮箱 | ValidFuncs.IsEmail("test@example.com") |
| IsIP | IP地址 | ValidFuncs.IsIP("192.168.1.1") |
| IsPhoneNumber | 国际电话 | ValidFuncs.IsPhoneNumber("+86-138-1234-5678") |
| IsUrl | URL | ValidFuncs.IsUrl("https://example.com") |
| IsHash | 哈希值 | ValidFuncs.IsHash("abc123", "md5") |
| IsNotEmpty | 非空 | ValidFuncs.IsNotEmpty("test") |
| Equals | 相等 | ValidFuncs.Equals("a", "a") |
| NotEquals | 不相等 | ValidFuncs.NotEquals("a", "b") |
| Contains | 包含 | ValidFuncs.Contains("hello", "ell") |
| IsIn | 在数组中 | ValidFuncs.IsIn("a", ["a", "b"]) |
| IsNotIn | 不在数组中 | ValidFuncs.IsNotIn("c", ["a", "b"]) |
| IsDate | 日期 | ValidFuncs.IsDate(new Date()) |
| Gt | 大于 | ValidFuncs.Gt(10, 5) |
| Gte | 大于等于 | ValidFuncs.Gte(10, 10) |
| Lt | 小于 | ValidFuncs.Lt(5, 10) |
| Lte | 小于等于 | ValidFuncs.Lte(10, 10) |
📊 测试覆盖率
当前测试覆盖率:76%+
- 语句覆盖率: 76.23%
- 分支覆盖率: 77.92%
- 函数覆盖率: 69.62%
- 行覆盖率: 76.14%
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📄 许可证
BSD-3-Clause License
🔗 相关项目