schema-dsl 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. package/src/validators/CustomKeywords.ts +477 -477
@@ -1,502 +1,502 @@
1
- # 数据验证最佳实践指南
2
-
3
- > **用途**: 完整的数据验证使用指南
4
- > **阅读时间**: 15分钟
5
-
6
- ---
7
-
8
- ## 📑 目录
9
-
10
- - [快速入门](#快速入门)
11
- - [DSL 语法速查](#dsl-语法速查)
12
- - [验证模式](#验证模式)
13
- - [错误处理](#错误处理)
14
- - [性能优化](#性能优化)
15
- - [常见场景](#常见场景)
16
- - [最佳实践](#最佳实践)
17
-
18
- ---
19
-
20
- ## 快速入门
21
-
22
- ### 基本验证流程
23
-
24
- ```javascript
25
- const { dsl, validate } = require('schema-dsl');
26
-
27
- // 1. 定义 Schema
28
- const schema = dsl({
29
- username: 'string:3-32!',
30
- email: 'email!',
31
- age: 'number:18-120'
32
- });
33
-
34
- // 2. 验证数据
35
- const result = validate(schema, {
36
- username: 'john_doe',
37
- email: 'john@example.com',
38
- age: 25
39
- });
40
-
41
- // 3. 处理结果
42
- if (result.valid) {
43
- console.log('验证通过', result.data);
44
- } else {
45
- console.log('验证失败', result.errors);
46
- }
47
- ```
48
-
49
- ---
50
-
51
- ## DSL 语法速查
52
-
53
- ### 基本类型
54
-
55
- | DSL | 说明 |
56
- |-----|------|
57
- | `'string'` | 字符串 |
58
- | `'number'` | 数字 |
59
- | `'integer'` | 整数 |
60
- | `'boolean'` | 布尔值 |
61
- | `'object'` | 对象 |
62
- | `'array'` | 数组 |
63
-
64
- ### 格式类型
65
-
66
- | DSL | 说明 |
67
- |-----|------|
68
- | `'email'` | 邮箱格式 |
69
- | `'url'` | URL 格式 |
70
- | `'uuid'` | UUID 格式 |
71
- | `'date'` | 日期格式 |
72
- | `'datetime'` | 日期时间格式 |
73
- | `'time'` | 时间格式 |
74
- | `'ipv4'` | IPv4 地址 |
75
- | `'ipv6'` | IPv6 地址 |
76
-
77
- ### 约束语法
78
-
79
- | DSL | 说明 |
80
- |-----|------|
81
- | `'string:10'` | 最大长度 10 |
82
- | `'string:3-32'` | 长度 3-32 |
83
- | `'string:3-'` | 最小长度 3 |
84
- | `'number:18-120'` | 数值范围 18-120 |
85
- | `'array:1-10'` | 数组长度 1-10 |
86
-
87
- ### 特殊标记
88
-
89
- | DSL | 说明 |
90
- |-----|------|
91
- | `'string!'` | 必填字符串 |
92
- | `'email!'` | 必填邮箱 |
93
- | `'a\|b\|c'` | 枚举值 |
94
- | `'array<string>'` | 字符串数组 |
95
-
96
- ---
97
-
98
- ## 验证模式
99
-
100
- ### 1. 便捷函数验证(推荐)
101
-
102
- 最简单的验证方式,使用内置单例 Validator:
103
-
104
- ```javascript
105
- const { dsl, validate } = require('schema-dsl');
106
-
107
- const result = validate(schema, data);
108
- ```
109
-
110
- ### 2. Validator 实例验证(高级)
111
-
112
- 需要自定义配置(如类型转换、自定义关键字)时使用:
113
-
114
- ```javascript
115
- const { dsl, Validator } = require('schema-dsl');
116
-
117
- // 创建自定义配置的 Validator
118
- const validator = new Validator({
119
- allErrors: true, // 返回所有错误
120
- useDefaults: true, // 使用默认值
121
- coerceTypes: true // ✨ 启用类型转换
122
- });
123
-
124
- const result = validator.validate(schema, data);
125
- ```
126
-
127
- > **注意**: `new Validator()` 会创建一个新的 Ajv 实例,有一定的初始化开销。建议在应用启动时创建并复用,避免在每次请求中创建。
128
-
129
- ### 3. 预编译验证(高性能)
130
-
131
- 频繁验证同一 Schema 时使用:
132
-
133
- ```javascript
134
- const validator = new Validator();
135
-
136
- // 预编译 Schema
137
- const validateUser = validator.compile(userSchema);
138
-
139
- // 多次验证(无需重复编译)
140
- const result1 = validateUser(data1);
141
- const result2 = validateUser(data2);
142
- const result3 = validateUser(data3);
143
- ```
144
-
145
- ### 4. 批量验证
146
-
147
- 验证多条数据时使用:
148
-
149
- ```javascript
150
- const { Validator } = require('schema-dsl');
151
- const validator = new Validator();
152
-
153
- const dataList = [
154
- { username: 'user1', email: 'user1@example.com' },
155
- { username: 'user2', email: 'invalid' },
156
- { username: 'u', email: 'user3@example.com' }
157
- ];
158
-
159
- const results = validator.validateBatch(schema, dataList);
160
- // [
161
- // { valid: true, data: {...}, errors: [] },
162
- // { valid: false, data: {...}, errors: [...] },
163
- // { valid: false, data: {...}, errors: [...] }
164
- // ]
165
- ```
166
-
167
- ---
168
-
169
- ## 错误处理
170
-
171
- ### 错误对象结构
172
-
173
- ```javascript
174
- {
175
- message: '用户名长度不能少于3个字符',
176
- path: '/username',
177
- keyword: 'minLength',
178
- params: { limit: 3 }
179
- }
180
- ```
181
-
182
- ### 自定义错误消息
183
-
184
- ```javascript
185
- const schema = dsl({
186
- username: 'string:3-32!'
187
- .label('用户名')
188
- .messages({
189
- 'min': '{{#label}}太短了,至少{{#limit}}个字符',
190
- 'max': '{{#label}}太长了,最多{{#limit}}个字符',
191
- 'required': '请输入{{#label}}'
192
- })
193
- });
194
- ```
195
-
196
- ### 多语言错误消息
197
-
198
- ```javascript
199
- const { Locale, Validator } = require('schema-dsl');
200
-
201
- // 添加语言包
202
- Locale.addLocale('zh-CN', {
203
- 'required': '{{#label}}不能为空',
204
- 'min': '{{#label}}长度不能少于{{#limit}}',
205
- 'email': '请输入有效的{{#label}}'
206
- });
207
-
208
- // 验证时指定语言
209
- const validator = new Validator();
210
- const result = validator.validate(schema, data, { locale: 'zh-CN' });
211
- ```
212
-
213
- ### 错误格式化
214
-
215
- ```javascript
216
- function formatErrors(errors) {
217
- return errors.map(err => {
218
- const field = err.path.replace(/^\//, '').replace(/\//g, '.');
219
- return `[${field}] ${err.message}`;
220
- }).join('\n');
221
- }
222
-
223
- if (!result.valid) {
224
- console.log(formatErrors(result.errors));
225
- // [username] 用户名长度不能少于3个字符
226
- // [email] 请输入有效的邮箱地址
227
- }
228
- ```
229
-
230
- ---
231
-
232
- ## 性能优化
233
-
234
- ### 1. 使用预编译
235
-
236
- ```javascript
237
- // ❌ 每次都编译(慢)
238
- function validateUser(data) {
239
- return validate(userSchema, data);
240
- }
241
-
242
- // ✅ 预编译一次,多次使用(快)
243
- const validator = new Validator();
244
- const validateUser = validator.compile(userSchema);
245
- ```
246
-
247
- ### 2. 缓存 Schema
248
-
249
- ```javascript
250
- // ❌ 每次都创建 Schema
251
- function getSchema() {
252
- return dsl({
253
- username: 'string:3-32!',
254
- email: 'email!'
255
- });
256
- }
257
-
258
- // ✅ 缓存 Schema
259
- const userSchema = dsl({
260
- username: 'string:3-32!',
261
- email: 'email!'
262
- });
263
- ```
264
-
265
- ### 3. 合理使用 allErrors
266
-
267
- ```javascript
268
- // 只需要第一个错误时
269
- const validator = new Validator({ allErrors: false });
270
-
271
- // 需要所有错误时(默认)
272
- const validator = new Validator({ allErrors: true });
273
- ```
274
-
275
- ### 4. 监控性能
276
-
277
- ```javascript
278
- console.time('schema-dsl.validate');
279
- const result = validate(schema, data);
280
- console.timeEnd('schema-dsl.validate');
281
- ```
282
-
283
- ---
284
-
285
- ## 常见场景
286
-
287
- ### 用户注册表单
288
-
289
- ```javascript
290
- const registerSchema = dsl({
291
- username: 'string:3-32!'
292
- .pattern(/^[a-zA-Z0-9_]+$/)
293
- .label('用户名')
294
- .messages({
295
- 'pattern': '{{#label}}只能包含字母、数字和下划线'
296
- }),
297
-
298
- email: 'email!'
299
- .label('邮箱地址'),
300
-
301
- password: 'string:8-64!'
302
- .password('strong')
303
- .label('密码'),
304
-
305
- age: 'number:18-120'
306
- .label('年龄'),
307
-
308
- gender: 'male|female|other',
309
-
310
- terms: 'boolean!'
311
- .label('服务条款')
312
- .messages({
313
- 'required': '请同意{{#label}}'
314
- })
315
- });
316
- ```
317
-
318
- ### API 请求验证
319
-
320
- ```javascript
321
- const createOrderSchema = dsl({
322
- userId: 'string!',
323
- items: 'array!1-100',
324
- shippingAddress: {
325
- street: 'string:5-200!',
326
- city: 'string:2-100!',
327
- zipCode: 'string:5-10!',
328
- country: 'string:2!'
329
- },
330
- paymentMethod: 'credit_card|paypal|bank_transfer',
331
- notes: 'string:500'
332
- });
333
-
334
- // Express 中间件
335
- function validateRequest(schema) {
336
- return (req, res, next) => {
337
- const result = validate(schema, req.body);
338
- if (!result.valid) {
339
- return res.status(400).json({ errors: result.errors });
340
- }
341
- req.validatedData = result.data;
342
- next();
343
- };
344
- }
345
-
346
- app.post('/orders', validateRequest(createOrderSchema), createOrder);
347
- ```
348
-
349
- ### 配置文件验证
350
-
351
- ```javascript
352
- const configSchema = dsl({
353
- server: {
354
- host: 'string!',
355
- port: 'integer:1-65535!',
356
- ssl: 'boolean'
357
- },
358
- database: {
359
- url: 'url!',
360
- poolSize: 'integer:1-100',
361
- timeout: 'integer:1000-60000'
362
- },
363
- logging: {
364
- level: 'debug|info|warn|error',
365
- format: 'json|text'
366
- }
367
- });
368
-
369
- function loadConfig(configPath) {
370
- const config = require(configPath);
371
- const result = validate(configSchema, config);
372
-
373
- if (!result.valid) {
374
- throw new Error(`配置文件错误:\n${formatErrors(result.errors)}`);
375
- }
376
-
377
- return result.data;
378
- }
379
- ```
380
-
381
- ---
382
-
383
- ## 最佳实践
384
-
385
- ### 1. 使用 label 提升错误消息质量
386
-
387
- ```javascript
388
- // ❌ 默认错误消息
389
- email: 'email!'
390
- // 错误: "email is required"
391
-
392
- // ✅ 使用 label
393
- email: 'email!'.label('邮箱地址')
394
- // 错误: "邮箱地址不能为空"
395
- ```
396
-
397
- ### 2. 集中管理 Schema
398
-
399
- ```javascript
400
- // schemas/index.js
401
- const { dsl } = require('schema-dsl');
402
-
403
- exports.userSchema = dsl({
404
- username: 'string:3-32!',
405
- email: 'email!'
406
- });
407
-
408
- exports.orderSchema = dsl({
409
- userId: 'string!',
410
- items: 'array!1-100'
411
- });
412
- ```
413
-
414
- ### 3. 使用 SchemaUtils 复用字段
415
-
416
- ```javascript
417
- const { SchemaUtils, dsl } = require('schema-dsl');
418
-
419
- // 创建可复用字段
420
- const emailField = SchemaUtils.reusable(() =>
421
- dsl('email!').label('邮箱地址')
422
- );
423
-
424
- // 在多个 Schema 中复用
425
- const loginSchema = dsl({ email: emailField() });
426
- const registerSchema = dsl({ email: emailField(), name: 'string!' });
427
- ```
428
-
429
- ### 4. 分层验证
430
-
431
- ```javascript
432
- // 基础验证(快速)
433
- const quickSchema = dsl({
434
- username: 'string!',
435
- email: 'string!'
436
- });
437
-
438
- // 完整验证(详细)
439
- const fullSchema = dsl({
440
- username: 'string:3-32!'.pattern(/^[a-z]+$/),
441
- email: 'email!'
442
- });
443
-
444
- // 先快速验证,再完整验证
445
- async function validateWithFallback(data) {
446
- const quick = validate(quickSchema, data);
447
- if (!quick.valid) return quick;
448
-
449
- const full = validate(fullSchema, data);
450
- if (!full.valid) return full;
451
-
452
- if (await checkEmailUnique(data.email)) {
453
- return {
454
- valid: false,
455
- errors: [{ field: 'email', keyword: 'business', message: '邮箱已被占用' }]
456
- };
457
- }
458
-
459
- return full;
460
- }
461
- ```
462
-
463
- ---
464
-
465
- ## 对应示例文件
466
-
467
- **示例入口**: [validation-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validation-guide.ts)
468
- **说明**: 覆盖推荐的验证流程:定义可复用 schema、格式化错误、预编译复用以及批量验证。
469
-
470
- ### 5. 测试验证逻辑
471
-
472
- ```javascript
473
- describe('User Schema', () => {
474
- it('应该验证有效用户', () => {
475
- const result = validate(userSchema, {
476
- username: 'john_doe',
477
- email: 'john@example.com'
478
- });
479
- expect(result.valid).to.be.true;
480
- });
481
-
482
- it('应该拒绝短用户名', () => {
483
- const result = validate(userSchema, {
484
- username: 'ab',
485
- email: 'john@example.com'
486
- });
487
- expect(result.valid).to.be.false;
488
- expect(result.errors[0].keyword).to.equal('minLength');
489
- });
490
- });
491
- ```
492
-
493
- ---
494
-
495
- ## 相关文档
496
-
497
- - [DSL 语法完整指南](dsl-syntax.md)
498
- - [validate 方法详解](validate.md)
499
- - [错误处理指南](error-handling.md)
500
- - [多语言支持](dynamic-locale.md)
501
- - [String 扩展](string-extensions.md)
502
-
1
+ # 数据验证最佳实践指南
2
+
3
+ > **用途**: 完整的数据验证使用指南
4
+ > **阅读时间**: 15分钟
5
+
6
+ ---
7
+
8
+ ## 📑 目录
9
+
10
+ - [快速入门](#快速入门)
11
+ - [DSL 语法速查](#dsl-语法速查)
12
+ - [验证模式](#验证模式)
13
+ - [错误处理](#错误处理)
14
+ - [性能优化](#性能优化)
15
+ - [常见场景](#常见场景)
16
+ - [最佳实践](#最佳实践)
17
+
18
+ ---
19
+
20
+ ## 快速入门
21
+
22
+ ### 基本验证流程
23
+
24
+ ```javascript
25
+ const { dsl, validate } = require('schema-dsl');
26
+
27
+ // 1. 定义 Schema
28
+ const schema = dsl({
29
+ username: 'string:3-32!',
30
+ email: 'email!',
31
+ age: 'number:18-120'
32
+ });
33
+
34
+ // 2. 验证数据
35
+ const result = validate(schema, {
36
+ username: 'john_doe',
37
+ email: 'john@example.com',
38
+ age: 25
39
+ });
40
+
41
+ // 3. 处理结果
42
+ if (result.valid) {
43
+ console.log('验证通过', result.data);
44
+ } else {
45
+ console.log('验证失败', result.errors);
46
+ }
47
+ ```
48
+
49
+ ---
50
+
51
+ ## DSL 语法速查
52
+
53
+ ### 基本类型
54
+
55
+ | DSL | 说明 |
56
+ |-----|------|
57
+ | `'string'` | 字符串 |
58
+ | `'number'` | 数字 |
59
+ | `'integer'` | 整数 |
60
+ | `'boolean'` | 布尔值 |
61
+ | `'object'` | 对象 |
62
+ | `'array'` | 数组 |
63
+
64
+ ### 格式类型
65
+
66
+ | DSL | 说明 |
67
+ |-----|------|
68
+ | `'email'` | 邮箱格式 |
69
+ | `'url'` | URL 格式 |
70
+ | `'uuid'` | UUID 格式 |
71
+ | `'date'` | 日期格式 |
72
+ | `'datetime'` | 日期时间格式 |
73
+ | `'time'` | 时间格式 |
74
+ | `'ipv4'` | IPv4 地址 |
75
+ | `'ipv6'` | IPv6 地址 |
76
+
77
+ ### 约束语法
78
+
79
+ | DSL | 说明 |
80
+ |-----|------|
81
+ | `'string:10'` | 最大长度 10 |
82
+ | `'string:3-32'` | 长度 3-32 |
83
+ | `'string:3-'` | 最小长度 3 |
84
+ | `'number:18-120'` | 数值范围 18-120 |
85
+ | `'array:1-10'` | 数组长度 1-10 |
86
+
87
+ ### 特殊标记
88
+
89
+ | DSL | 说明 |
90
+ |-----|------|
91
+ | `'string!'` | 必填字符串 |
92
+ | `'email!'` | 必填邮箱 |
93
+ | `'a\|b\|c'` | 枚举值 |
94
+ | `'array<string>'` | 字符串数组 |
95
+
96
+ ---
97
+
98
+ ## 验证模式
99
+
100
+ ### 1. 便捷函数验证(推荐)
101
+
102
+ 最简单的验证方式,使用内置单例 Validator:
103
+
104
+ ```javascript
105
+ const { dsl, validate } = require('schema-dsl');
106
+
107
+ const result = validate(schema, data);
108
+ ```
109
+
110
+ ### 2. Validator 实例验证(高级)
111
+
112
+ 需要自定义配置(如类型转换、自定义关键字)时使用:
113
+
114
+ ```javascript
115
+ const { dsl, Validator } = require('schema-dsl');
116
+
117
+ // 创建自定义配置的 Validator
118
+ const validator = new Validator({
119
+ allErrors: true, // 返回所有错误
120
+ useDefaults: true, // 使用默认值
121
+ coerceTypes: true // ✨ 启用类型转换
122
+ });
123
+
124
+ const result = validator.validate(schema, data);
125
+ ```
126
+
127
+ > **注意**: `new Validator()` 会创建一个新的 Ajv 实例,有一定的初始化开销。建议在应用启动时创建并复用,避免在每次请求中创建。
128
+
129
+ ### 3. 预编译验证(高性能)
130
+
131
+ 频繁验证同一 Schema 时使用:
132
+
133
+ ```javascript
134
+ const validator = new Validator();
135
+
136
+ // 预编译 Schema
137
+ const validateUser = validator.compile(userSchema);
138
+
139
+ // 多次验证(无需重复编译)
140
+ const result1 = validateUser(data1);
141
+ const result2 = validateUser(data2);
142
+ const result3 = validateUser(data3);
143
+ ```
144
+
145
+ ### 4. 批量验证
146
+
147
+ 验证多条数据时使用:
148
+
149
+ ```javascript
150
+ const { Validator } = require('schema-dsl');
151
+ const validator = new Validator();
152
+
153
+ const dataList = [
154
+ { username: 'user1', email: 'user1@example.com' },
155
+ { username: 'user2', email: 'invalid' },
156
+ { username: 'u', email: 'user3@example.com' }
157
+ ];
158
+
159
+ const results = validator.validateBatch(schema, dataList);
160
+ // [
161
+ // { valid: true, data: {...}, errors: [] },
162
+ // { valid: false, data: {...}, errors: [...] },
163
+ // { valid: false, data: {...}, errors: [...] }
164
+ // ]
165
+ ```
166
+
167
+ ---
168
+
169
+ ## 错误处理
170
+
171
+ ### 错误对象结构
172
+
173
+ ```javascript
174
+ {
175
+ message: '用户名长度不能少于3个字符',
176
+ path: '/username',
177
+ keyword: 'minLength',
178
+ params: { limit: 3 }
179
+ }
180
+ ```
181
+
182
+ ### 自定义错误消息
183
+
184
+ ```javascript
185
+ const schema = dsl({
186
+ username: 'string:3-32!'
187
+ .label('用户名')
188
+ .messages({
189
+ 'min': '{{#label}}太短了,至少{{#limit}}个字符',
190
+ 'max': '{{#label}}太长了,最多{{#limit}}个字符',
191
+ 'required': '请输入{{#label}}'
192
+ })
193
+ });
194
+ ```
195
+
196
+ ### 多语言错误消息
197
+
198
+ ```javascript
199
+ const { Locale, Validator } = require('schema-dsl');
200
+
201
+ // 添加语言包
202
+ Locale.addLocale('zh-CN', {
203
+ 'required': '{{#label}}不能为空',
204
+ 'min': '{{#label}}长度不能少于{{#limit}}',
205
+ 'email': '请输入有效的{{#label}}'
206
+ });
207
+
208
+ // 验证时指定语言
209
+ const validator = new Validator();
210
+ const result = validator.validate(schema, data, { locale: 'zh-CN' });
211
+ ```
212
+
213
+ ### 错误格式化
214
+
215
+ ```javascript
216
+ function formatErrors(errors) {
217
+ return errors.map(err => {
218
+ const field = err.path.replace(/^\//, '').replace(/\//g, '.');
219
+ return `[${field}] ${err.message}`;
220
+ }).join('\n');
221
+ }
222
+
223
+ if (!result.valid) {
224
+ console.log(formatErrors(result.errors));
225
+ // [username] 用户名长度不能少于3个字符
226
+ // [email] 请输入有效的邮箱地址
227
+ }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## 性能优化
233
+
234
+ ### 1. 使用预编译
235
+
236
+ ```javascript
237
+ // ❌ 每次都编译(慢)
238
+ function validateUser(data) {
239
+ return validate(userSchema, data);
240
+ }
241
+
242
+ // ✅ 预编译一次,多次使用(快)
243
+ const validator = new Validator();
244
+ const validateUser = validator.compile(userSchema);
245
+ ```
246
+
247
+ ### 2. 缓存 Schema
248
+
249
+ ```javascript
250
+ // ❌ 每次都创建 Schema
251
+ function getSchema() {
252
+ return dsl({
253
+ username: 'string:3-32!',
254
+ email: 'email!'
255
+ });
256
+ }
257
+
258
+ // ✅ 缓存 Schema
259
+ const userSchema = dsl({
260
+ username: 'string:3-32!',
261
+ email: 'email!'
262
+ });
263
+ ```
264
+
265
+ ### 3. 合理使用 allErrors
266
+
267
+ ```javascript
268
+ // 只需要第一个错误时
269
+ const validator = new Validator({ allErrors: false });
270
+
271
+ // 需要所有错误时(默认)
272
+ const validator = new Validator({ allErrors: true });
273
+ ```
274
+
275
+ ### 4. 监控性能
276
+
277
+ ```javascript
278
+ console.time('schema-dsl.validate');
279
+ const result = validate(schema, data);
280
+ console.timeEnd('schema-dsl.validate');
281
+ ```
282
+
283
+ ---
284
+
285
+ ## 常见场景
286
+
287
+ ### 用户注册表单
288
+
289
+ ```javascript
290
+ const registerSchema = dsl({
291
+ username: 'string:3-32!'
292
+ .pattern(/^[a-zA-Z0-9_]+$/)
293
+ .label('用户名')
294
+ .messages({
295
+ 'pattern': '{{#label}}只能包含字母、数字和下划线'
296
+ }),
297
+
298
+ email: 'email!'
299
+ .label('邮箱地址'),
300
+
301
+ password: 'string:8-64!'
302
+ .password('strong')
303
+ .label('密码'),
304
+
305
+ age: 'number:18-120'
306
+ .label('年龄'),
307
+
308
+ gender: 'male|female|other',
309
+
310
+ terms: 'boolean!'
311
+ .label('服务条款')
312
+ .messages({
313
+ 'required': '请同意{{#label}}'
314
+ })
315
+ });
316
+ ```
317
+
318
+ ### API 请求验证
319
+
320
+ ```javascript
321
+ const createOrderSchema = dsl({
322
+ userId: 'string!',
323
+ items: 'array!1-100',
324
+ shippingAddress: {
325
+ street: 'string:5-200!',
326
+ city: 'string:2-100!',
327
+ zipCode: 'string:5-10!',
328
+ country: 'string:2!'
329
+ },
330
+ paymentMethod: 'credit_card|paypal|bank_transfer',
331
+ notes: 'string:500'
332
+ });
333
+
334
+ // Express 中间件
335
+ function validateRequest(schema) {
336
+ return (req, res, next) => {
337
+ const result = validate(schema, req.body);
338
+ if (!result.valid) {
339
+ return res.status(400).json({ errors: result.errors });
340
+ }
341
+ req.validatedData = result.data;
342
+ next();
343
+ };
344
+ }
345
+
346
+ app.post('/orders', validateRequest(createOrderSchema), createOrder);
347
+ ```
348
+
349
+ ### 配置文件验证
350
+
351
+ ```javascript
352
+ const configSchema = dsl({
353
+ server: {
354
+ host: 'string!',
355
+ port: 'integer:1-65535!',
356
+ ssl: 'boolean'
357
+ },
358
+ database: {
359
+ url: 'url!',
360
+ poolSize: 'integer:1-100',
361
+ timeout: 'integer:1000-60000'
362
+ },
363
+ logging: {
364
+ level: 'debug|info|warn|error',
365
+ format: 'json|text'
366
+ }
367
+ });
368
+
369
+ function loadConfig(configPath) {
370
+ const config = require(configPath);
371
+ const result = validate(configSchema, config);
372
+
373
+ if (!result.valid) {
374
+ throw new Error(`配置文件错误:\n${formatErrors(result.errors)}`);
375
+ }
376
+
377
+ return result.data;
378
+ }
379
+ ```
380
+
381
+ ---
382
+
383
+ ## 最佳实践
384
+
385
+ ### 1. 使用 label 提升错误消息质量
386
+
387
+ ```javascript
388
+ // ❌ 默认错误消息
389
+ email: 'email!'
390
+ // 错误: "email is required"
391
+
392
+ // ✅ 使用 label
393
+ email: 'email!'.label('邮箱地址')
394
+ // 错误: "邮箱地址不能为空"
395
+ ```
396
+
397
+ ### 2. 集中管理 Schema
398
+
399
+ ```javascript
400
+ // schemas/index.js
401
+ const { dsl } = require('schema-dsl');
402
+
403
+ exports.userSchema = dsl({
404
+ username: 'string:3-32!',
405
+ email: 'email!'
406
+ });
407
+
408
+ exports.orderSchema = dsl({
409
+ userId: 'string!',
410
+ items: 'array!1-100'
411
+ });
412
+ ```
413
+
414
+ ### 3. 使用 SchemaUtils 复用字段
415
+
416
+ ```javascript
417
+ const { SchemaUtils, dsl } = require('schema-dsl');
418
+
419
+ // 创建可复用字段
420
+ const emailField = SchemaUtils.reusable(() =>
421
+ dsl('email!').label('邮箱地址')
422
+ );
423
+
424
+ // 在多个 Schema 中复用
425
+ const loginSchema = dsl({ email: emailField() });
426
+ const registerSchema = dsl({ email: emailField(), name: 'string!' });
427
+ ```
428
+
429
+ ### 4. 分层验证
430
+
431
+ ```javascript
432
+ // 基础验证(快速)
433
+ const quickSchema = dsl({
434
+ username: 'string!',
435
+ email: 'string!'
436
+ });
437
+
438
+ // 完整验证(详细)
439
+ const fullSchema = dsl({
440
+ username: 'string:3-32!'.pattern(/^[a-z]+$/),
441
+ email: 'email!'
442
+ });
443
+
444
+ // 先快速验证,再完整验证
445
+ async function validateWithFallback(data) {
446
+ const quick = validate(quickSchema, data);
447
+ if (!quick.valid) return quick;
448
+
449
+ const full = validate(fullSchema, data);
450
+ if (!full.valid) return full;
451
+
452
+ if (await checkEmailUnique(data.email)) {
453
+ return {
454
+ valid: false,
455
+ errors: [{ field: 'email', keyword: 'business', message: '邮箱已被占用' }]
456
+ };
457
+ }
458
+
459
+ return full;
460
+ }
461
+ ```
462
+
463
+ ---
464
+
465
+ ## 对应示例文件
466
+
467
+ **示例入口**: [validation-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validation-guide.ts)
468
+ **说明**: 覆盖推荐的验证流程:定义可复用 schema、格式化错误、预编译复用以及批量验证。
469
+
470
+ ### 5. 测试验证逻辑
471
+
472
+ ```javascript
473
+ describe('User Schema', () => {
474
+ it('应该验证有效用户', () => {
475
+ const result = validate(userSchema, {
476
+ username: 'john_doe',
477
+ email: 'john@example.com'
478
+ });
479
+ expect(result.valid).to.be.true;
480
+ });
481
+
482
+ it('应该拒绝短用户名', () => {
483
+ const result = validate(userSchema, {
484
+ username: 'ab',
485
+ email: 'john@example.com'
486
+ });
487
+ expect(result.valid).to.be.false;
488
+ expect(result.errors[0].keyword).to.equal('minLength');
489
+ });
490
+ });
491
+ ```
492
+
493
+ ---
494
+
495
+ ## 相关文档
496
+
497
+ - [DSL 语法完整指南](dsl-syntax.md)
498
+ - [validate 方法详解](validate.md)
499
+ - [错误处理指南](error-handling.md)
500
+ - [多语言支持](dynamic-locale.md)
501
+ - [String 扩展](string-extensions.md)
502
+