用途: 帮助你写出高质量、高性能的 Schema 代码
更新: 2025-12-26
推荐:
const schema = dsl({
username: 'string:3-32!',
age: 'number:18-120',
email: 'email!',
role: 'admin|user|guest'
});不推荐(过度复杂):
const schema = dsl({
username: dsl('string').minLength(3).maxLength(32).required(),
// 太冗长了!
});原则: 能用 DSL 字符串表达的,就不要用链式调用。
适合链式调用的场景:
- 需要正则验证
- 需要自定义错误消息
- 需要自定义验证器
- 需要标签(label)
示例:
const schema = dsl({
// 简单字段:纯 DSL
age: 'number:18-120',
// 复杂字段:链式调用
username: 'string:3-32!'
.pattern(/^[a-zA-Z0-9_]+$/)
.label('用户名')
.messages({
'pattern': '只能包含字母、数字和下划线',
'min': '至少3个字符',
'max': '最多32个字符'
}),
email: 'email!'
.custom(async (value) => {
const exists = await checkEmailExists(value);
if (exists) return '邮箱已被占用';
})
.label('邮箱地址')
});SchemaI-DSL 提供了常用的预设验证器,开箱即用:
const schema = dsl({
// ✅ 使用预设验证器(推荐)
username: dsl('string!').username(), // 自动设置 3-32 长度 + 正则
password: dsl('string!').password('strong'), // 强密码验证
phone: dsl('string!').phone('cn'), // 中国手机号
// ❌ 手动实现(不推荐)
username: 'string:3-32!'
.pattern(/^[a-zA-Z][a-zA-Z0-9_]*$/)
});可用预设:
username(preset?)- 用户名验证password(strength?)- 密码强度验证phone(country?)- 手机号验证slug()- URL slug 验证
不推荐(嵌套过深):
const schema = dsl({
user: {
profile: {
personal: {
address: {
detail: {
street: 'string' // 嵌套 5 层
}
}
}
}
}
});推荐(拆分或扁平化):
// 方案1: 拆分为多个 Schema
const addressSchema = dsl({
street: 'string!',
city: 'string!',
zipCode: 'string'
});
const userSchema = dsl({
name: 'string!',
email: 'email!',
address: addressSchema
});
// 方案2: 扁平化
const schema = dsl({
'user_name': 'string!',
'user_email': 'email!',
'address_street': 'string!',
'address_city': 'string!'
});原则: 嵌套深度建议不超过 3-4 层。
不推荐(每次都编译):
app.post('/api/user', (req, res) => {
const schema = dsl({ username: 'string!' });
const result = validate(schema, req.body); // 每次都编译
});推荐(预编译):
// 在应用启动时编译一次
const userSchema = dsl({ username: 'string!' });
const validateUser = validator.compile(userSchema);
app.post('/api/user', (req, res) => {
const result = validateUser(req.body); // 直接使用
});性能提升: 预编译可以提升 10-100 倍 的性能!
const validator = new Validator({
cache: true // 启用编译缓存
});
// 或者使用全局单例(默认启用缓存)
const { validate } = require('schema-dsl');
validate(schema, data); // 自动缓存不推荐(循环验证):
const errors = [];
records.forEach(record => {
const result = validate(schema, record);
if (!result.valid) {
errors.push(result.errors);
}
});推荐(批量验证):
const result = validator.validateBatch(schema, records);
// 一次性验证所有记录,性能更好不推荐(可能导致 ReDoS):
// 危险的正则:灾难性回溯
.pattern(/^(a+)+$/)
.pattern(/^(a*)*$/)
.pattern(/^(a|a)*$/)推荐(安全高效):
// 简单明确的正则
.pattern(/^[a-zA-Z0-9_]+$/)
.pattern(/^[a-z]{3,10}$/)工具: 使用 regexploit 检测危险正则。
不推荐:
records.forEach(record => {
const schema = dsl({ name: 'string!' }); // 每次都创建
validate(schema, record);
});推荐:
const schema = dsl({ name: 'string!' }); // 创建一次
records.forEach(record => {
validate(schema, record); // 重复使用
});危险:
// ❌ 用户控制的正则表达式
app.post('/api/validate', (req, res) => {
const pattern = req.body.pattern; // 用户输入
const schema = dsl('string').pattern(new RegExp(pattern)); // 危险!
});原因: 用户可能输入恶意正则导致 ReDoS 攻击。
安全做法:
// ✅ 使用预定义的正则
const ALLOWED_PATTERNS = {
username: /^[a-zA-Z0-9_]+$/,
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
};
app.post('/api/validate', (req, res) => {
const patternName = req.body.pattern;
const pattern = ALLOWED_PATTERNS[patternName];
if (!pattern) {
return res.status(400).json({ error: 'Invalid pattern' });
}
const schema = dsl('string').pattern(pattern);
});生产环境不要暴露敏感信息:
// 开发环境
if (process.env.NODE_ENV === 'development') {
return res.status(400).json({
valid: false,
errors: result.errors // 详细错误
});
}
// 生产环境
return res.status(400).json({
valid: false,
message: '输入数据验证失败' // 简化消息
});const validator = new Validator({
maxNestingDepth: 10, // 限制嵌套深度
maxSchemaSize: 10000 // 限制 Schema 大小(建议)
});
// 在 validate 前检查
DslBuilder.validateNestingDepth(schema, 10);// 验证数据时避免原型污染
const validator = new Validator({
removeAdditional: true, // 移除额外属性
useDefaults: false // 不自动填充默认值(如果不需要)
});推荐的错误处理中间件:
// Express 中间件
function validateMiddleware(schema) {
return (req, res, next) => {
const result = validate(schema, req.body);
if (!result.valid) {
return res.status(400).json({
code: 'VALIDATION_ERROR',
message: '请求数据验证失败',
errors: result.errors.map(err => ({
field: err.path,
message: err.message
}))
});
}
next();
};
}
// 使用
app.post('/api/user',
validateMiddleware(userSchema),
userController.create
);使用 label 和自定义消息:
const schema = dsl({
username: 'string:3-32!'
.label('用户名')
.messages({
'required': '{{#label}}不能为空',
'min': '{{#label}}至少需要{{#limit}}个字符',
'max': '{{#label}}最多{{#limit}}个字符',
'pattern': '{{#label}}格式不正确'
}),
email: 'email!'
.label('邮箱地址')
.messages({
'required': '请填写{{#label}}',
'format': '{{#label}}格式不正确'
})
});效果:
❌ 用户名不能为空
❌ 用户名至少需要3个字符
✅ 清晰明了,用户友好
const schema = dsl({
email: 'email!'.custom(async (value) => {
try {
const exists = await checkEmailExists(value);
if (exists) return '邮箱已被占用';
} catch (error) {
// 记录错误但不阻止验证
console.error('Email check failed:', error);
// 可以选择跳过此验证或返回提示
return; // 跳过
}
})
});推荐的项目结构:
src/
├── schemas/
│ ├── index.js # 导出所有 Schema
│ ├── user.schema.js # 用户相关 Schema
│ ├── post.schema.js # 文章相关 Schema
│ └── common.schema.js # 通用 Schema
├── routes/
│ ├── user.routes.js
│ └── post.routes.js
└── controllers/
schemas/user.schema.js:
const { dsl } = require('schema-dsl');
// 可复用的字段
const commonFields = {
username: dsl('string!').username().label('用户名'),
email: 'email!',
password: dsl('string!').password('strong').label('密码')
};
// 注册 Schema
exports.registerSchema = dsl({
...commonFields,
confirmPassword: 'string!',
agreeTerms: 'boolean!'
});
// 登录 Schema
exports.loginSchema = dsl({
email: commonFields.email,
password: commonFields.password
});
// 更新 Schema
exports.updateSchema = dsl({
username: commonFields.username,
email: commonFields.email
// 不包含密码
});schemas/index.js:
const userSchemas = require('./user.schema');
const postSchemas = require('./post.schema');
module.exports = {
user: userSchemas,
post: postSchemas
};routes/user.routes.js:
const schemas = require('../schemas');
const { validate } = require('schema-dsl');
router.post('/register', (req, res) => {
const result = validate(schemas.user.registerSchema, req.body);
// ...
});使用 SchemaHelper:
const { SchemaHelper } = require('schema-dsl');
// 创建可复用字段库
const fields = SchemaHelper.createLibrary({
email: 'email!',
phone: dsl('string!').phone('cn'),
password: dsl('string!').password('strong')
});
// 在多个 Schema 中复用
const registerSchema = dsl({
...fields.pick(['email', 'password']),
username: 'string:3-32!'
});
const profileSchema = dsl({
...fields.pick(['email', 'phone']),
bio: 'string:500'
});// config/validator.js
const { Validator } = require('schema-dsl');
const config = {
development: {
verbose: true,
allErrors: true,
cache: false // 开发时不缓存,便于调试
},
production: {
verbose: false,
allErrors: false, // 只返回第一个错误
cache: true // 生产环境启用缓存
}
};
module.exports = new Validator(
config[process.env.NODE_ENV || 'development']
);const validator = new Validator();
// 包装 validate 方法,添加监控
const originalValidate = validator.validate.bind(validator);
validator.validate = function(schema, data, options) {
const startTime = Date.now();
const result = originalValidate(schema, data, options);
const duration = Date.now() - startTime;
// 记录慢查询
if (duration > 100) {
console.warn(`Slow validation: ${duration}ms`);
}
// 记录验证失败
if (!result.valid) {
logger.info('Validation failed', {
errors: result.errors.length,
paths: result.errors.map(e => e.path)
});
}
return result;
};// routes/health.js
app.get('/health', (req, res) => {
const { validator } = require('../config/validator');
// 检查验证器是否正常
try {
const testSchema = dsl({ test: 'string!' });
const result = validator.validate(testSchema, { test: 'ok' });
if (!result.valid) {
throw new Error('Validator test failed');
}
res.json({
status: 'ok',
validator: 'operational',
cacheSize: validator.getCacheSize()
});
} catch (error) {
res.status(500).json({
status: 'error',
message: error.message
});
}
});// 定期清理缓存
const cron = require('node-cron');
// 每天凌晨清理一次
cron.schedule('0 0 * * *', () => {
validator.clearCache();
console.log('Validator cache cleared');
});
// 或者根据内存使用情况清理
setInterval(() => {
const memUsage = process.memoryUsage();
if (memUsage.heapUsed > 500 * 1024 * 1024) { // 超过 500MB
validator.clearCache();
}
}, 60000); // 每分钟检查一次基于 SchemaI-DSL 的性能测试:
| 操作 | 性能指标 |
|---|---|
| 简单验证(未缓存) | ~0.1ms |
| 简单验证(已缓存) | ~0.01ms |
| 复杂嵌套(未缓存) | ~1ms |
| 复杂嵌套(已缓存) | ~0.1ms |
| 批量验证(1000条) | ~100ms |
结论: 合理使用缓存可以提升 10-100倍 性能。
遵循这些最佳实践,你的 SchemaI-DSL 代码将具备:
✅ 高性能 - 通过预编译和缓存
✅ 高安全性 - 避免常见安全陷阱
✅ 高可维护性 - 清晰的代码组织
✅ 高可用性 - 完善的错误处理