schema-dsl 1.2.5 → 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 (243) hide show
  1. package/CHANGELOG.md +130 -238
  2. package/LICENSE +21 -21
  3. package/README.md +628 -2486
  4. package/dist/DslBuilder-BIgQOAXp.d.ts +343 -0
  5. package/dist/DslBuilder-CjHTucNQ.d.cts +343 -0
  6. package/dist/Validator-CllRdrY0.d.ts +192 -0
  7. package/dist/Validator-D6okG9tr.d.cts +192 -0
  8. package/dist/index.cjs +6640 -0
  9. package/dist/index.d.cts +1151 -0
  10. package/dist/index.d.ts +1151 -0
  11. package/dist/index.js +6574 -0
  12. package/dist/plugin-CIKtTMtS.d.cts +246 -0
  13. package/dist/plugin-CIKtTMtS.d.ts +246 -0
  14. package/dist/plugins/custom-format.cjs +3818 -0
  15. package/dist/plugins/custom-format.d.cts +12 -0
  16. package/dist/plugins/custom-format.d.ts +12 -0
  17. package/dist/plugins/custom-format.js +3788 -0
  18. package/dist/plugins/custom-type-example.cjs +3811 -0
  19. package/dist/plugins/custom-type-example.d.cts +8 -0
  20. package/dist/plugins/custom-type-example.d.ts +8 -0
  21. package/dist/plugins/custom-type-example.js +3781 -0
  22. package/dist/plugins/custom-validator.cjs +144 -0
  23. package/dist/plugins/custom-validator.d.cts +10 -0
  24. package/dist/plugins/custom-validator.d.ts +10 -0
  25. package/dist/plugins/custom-validator.js +119 -0
  26. package/docs/FEATURE-INDEX.md +553 -519
  27. package/docs/add-custom-locale.md +496 -483
  28. package/docs/add-keyword.md +24 -0
  29. package/docs/api-reference.md +1047 -805
  30. package/docs/api.md +13 -0
  31. package/docs/best-practices-project-structure.md +417 -408
  32. package/docs/best-practices.md +712 -672
  33. package/docs/cache-manager.md +344 -336
  34. package/docs/compile.md +45 -0
  35. package/docs/conditional-api.md +1307 -1278
  36. package/docs/custom-extensions-guide.md +339 -411
  37. package/docs/design-philosophy.md +606 -601
  38. package/docs/doc-index.md +324 -0
  39. package/docs/dsl-syntax.md +714 -664
  40. package/docs/dynamic-locale.md +608 -598
  41. package/docs/enum.md +482 -475
  42. package/docs/error-handling.md +1975 -1966
  43. package/docs/export-guide.md +501 -462
  44. package/docs/export-limitations.md +567 -551
  45. package/docs/faq.md +596 -577
  46. package/docs/frontend-i18n-guide.md +307 -293
  47. package/docs/i18n-user-guide.md +487 -474
  48. package/docs/i18n.md +476 -457
  49. package/docs/index.md +48 -0
  50. package/docs/json-schema-basics.md +40 -0
  51. package/docs/label-vs-description.md +271 -262
  52. package/docs/markdown-exporter.md +406 -397
  53. package/docs/mongodb-exporter.md +302 -295
  54. package/docs/multi-language.md +26 -0
  55. package/docs/multi-type-support.md +322 -329
  56. package/docs/mysql-exporter.md +280 -273
  57. package/docs/number-operators.md +449 -442
  58. package/docs/optional-marker-guide.md +326 -321
  59. package/docs/performance-guide.md +49 -0
  60. package/docs/plugin-system.md +381 -542
  61. package/docs/plugin-type-registration.md +34 -0
  62. package/docs/postgresql-exporter.md +311 -304
  63. package/docs/public/favicon.svg +5 -0
  64. package/docs/quick-start.md +435 -761
  65. package/docs/runtime-locale-support.md +532 -521
  66. package/docs/schema-helper.md +345 -340
  67. package/docs/schema-utils-advanced-issues.md +23 -0
  68. package/docs/schema-utils-best-practices.md +20 -0
  69. package/docs/schema-utils-chaining.md +150 -143
  70. package/docs/schema-utils.md +524 -490
  71. package/docs/security-checklist.md +20 -0
  72. package/docs/string-extensions.md +488 -480
  73. package/docs/troubleshooting.md +486 -471
  74. package/docs/type-converter.md +310 -319
  75. package/docs/type-reference.md +242 -219
  76. package/docs/typescript-guide.md +584 -573
  77. package/docs/union-type-guide.md +157 -147
  78. package/docs/union-types.md +284 -277
  79. package/docs/validate-async.md +491 -480
  80. package/docs/validate-batch.md +49 -0
  81. package/docs/validate-dsl-object-support.md +578 -573
  82. package/docs/validate.md +506 -486
  83. package/docs/validation-guide.md +502 -484
  84. package/docs/validator.md +39 -0
  85. package/package.json +131 -73
  86. package/plugins/custom-format.cjs +8 -0
  87. package/plugins/custom-type-example.cjs +8 -0
  88. package/plugins/custom-validator.cjs +8 -0
  89. package/src/adapters/DslAdapter.ts +111 -0
  90. package/src/adapters/index.ts +1 -0
  91. package/src/config/constants.ts +83 -0
  92. package/src/config/index.ts +2 -0
  93. package/src/config/patterns.ts +77 -0
  94. package/src/core/CacheManager.ts +169 -0
  95. package/src/core/ConditionalBuilder.ts +382 -0
  96. package/src/core/ConditionalRuntime.ts +28 -0
  97. package/src/core/ConditionalValidator.ts +255 -0
  98. package/src/core/DslBuilder.ts +687 -0
  99. package/src/core/ErrorCodes.ts +38 -0
  100. package/src/core/ErrorFormatter.ts +271 -0
  101. package/src/core/JSONSchemaCore.ts +65 -0
  102. package/src/core/Locale.ts +187 -0
  103. package/src/core/MessageTemplate.ts +42 -0
  104. package/src/core/ObjectDslBuilder.ts +64 -0
  105. package/src/core/PluginManager.ts +326 -0
  106. package/src/core/StringExtensions.ts +140 -0
  107. package/src/core/TemplateEngine.ts +44 -0
  108. package/src/core/Validator.ts +448 -0
  109. package/src/errors/I18nError.ts +159 -0
  110. package/src/errors/ValidationError.ts +105 -0
  111. package/src/exporters/BaseExporter.ts +60 -0
  112. package/src/exporters/MarkdownExporter.ts +305 -0
  113. package/src/exporters/MongoDBExporter.ts +126 -0
  114. package/src/exporters/MySQLExporter.ts +156 -0
  115. package/src/exporters/PostgreSQLExporter.ts +222 -0
  116. package/src/exporters/index.ts +18 -0
  117. package/src/index.ts +651 -0
  118. package/{lib/locales/en-US.js → src/locales/en-US.ts} +160 -176
  119. package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +160 -113
  120. package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +160 -113
  121. package/src/locales/index.ts +103 -0
  122. package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +160 -118
  123. package/src/locales/types.ts +156 -0
  124. package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +160 -177
  125. package/src/parser/ConstraintParser.ts +101 -0
  126. package/src/parser/DslParser.ts +470 -0
  127. package/src/parser/SchemaCompiler.ts +66 -0
  128. package/src/parser/TypeRegistry.ts +250 -0
  129. package/src/parser/index.ts +6 -0
  130. package/src/plugins/custom-format.ts +124 -0
  131. package/src/plugins/custom-type-example.ts +106 -0
  132. package/src/plugins/custom-validator.ts +138 -0
  133. package/src/types/conditional.ts +28 -0
  134. package/src/types/config.ts +59 -0
  135. package/src/types/dsl.ts +131 -0
  136. package/src/types/error.ts +60 -0
  137. package/src/types/index.ts +17 -0
  138. package/src/types/infer.ts +128 -0
  139. package/src/types/plugin.ts +58 -0
  140. package/src/types/safe-regex.d.ts +9 -0
  141. package/src/types/schema.ts +66 -0
  142. package/src/types/validate.ts +71 -0
  143. package/src/utils/SchemaHelper.ts +196 -0
  144. package/src/utils/SchemaUtils.ts +365 -0
  145. package/src/utils/TypeConverter.ts +215 -0
  146. package/src/utils/index.ts +10 -0
  147. package/src/validators/CustomKeywords.ts +477 -0
  148. package/.eslintignore +0 -11
  149. package/.eslintrc.json +0 -27
  150. package/CONTRIBUTING.md +0 -368
  151. package/STATUS.md +0 -491
  152. package/changelogs/v1.0.0.md +0 -328
  153. package/changelogs/v1.0.9.md +0 -367
  154. package/changelogs/v1.1.0.md +0 -389
  155. package/changelogs/v1.1.1.md +0 -308
  156. package/changelogs/v1.1.2.md +0 -183
  157. package/changelogs/v1.1.3.md +0 -161
  158. package/changelogs/v1.1.4.md +0 -432
  159. package/changelogs/v1.1.5.md +0 -493
  160. package/changelogs/v1.1.6.md +0 -211
  161. package/changelogs/v1.1.8.md +0 -376
  162. package/changelogs/v1.2.3.md +0 -124
  163. package/docs/INDEX.md +0 -252
  164. package/docs/issues-resolved-summary.md +0 -196
  165. package/docs/performance-benchmark-report.md +0 -179
  166. package/docs/performance-quick-reference.md +0 -123
  167. package/docs/user-questions-answered.md +0 -353
  168. package/docs/validation-rules-v1.0.2.md +0 -1608
  169. package/examples/README.md +0 -81
  170. package/examples/array-dsl-example.js +0 -227
  171. package/examples/conditional-example.js +0 -288
  172. package/examples/conditional-non-object.js +0 -129
  173. package/examples/conditional-validate-example.js +0 -321
  174. package/examples/custom-extension.js +0 -85
  175. package/examples/dsl-match-example.js +0 -74
  176. package/examples/dsl-style.js +0 -118
  177. package/examples/dynamic-locale-configuration.js +0 -348
  178. package/examples/dynamic-locale-example.js +0 -287
  179. package/examples/enum.examples.js +0 -324
  180. package/examples/export-demo.js +0 -130
  181. package/examples/express-integration.js +0 -376
  182. package/examples/i18n-error-handling-complete.js +0 -381
  183. package/examples/i18n-error-handling-quickstart.md +0 -0
  184. package/examples/i18n-error.examples.js +0 -181
  185. package/examples/i18n-full-demo.js +0 -301
  186. package/examples/i18n-memory-safety.examples.js +0 -268
  187. package/examples/markdown-export.js +0 -71
  188. package/examples/middleware-usage.js +0 -93
  189. package/examples/new-features-comparison.js +0 -315
  190. package/examples/password-reset/README.md +0 -153
  191. package/examples/password-reset/schema.js +0 -26
  192. package/examples/password-reset/test.js +0 -101
  193. package/examples/plugin-system.examples.js +0 -205
  194. package/examples/schema-utils-chaining.examples.js +0 -250
  195. package/examples/simple-example.js +0 -122
  196. package/examples/slug.examples.js +0 -179
  197. package/examples/string-extensions.js +0 -297
  198. package/examples/union-type-example.js +0 -127
  199. package/examples/union-types-example.js +0 -77
  200. package/examples/user-registration/README.md +0 -156
  201. package/examples/user-registration/routes.js +0 -92
  202. package/examples/user-registration/schema.js +0 -150
  203. package/examples/user-registration/server.js +0 -74
  204. package/index.d.ts +0 -3658
  205. package/index.js +0 -475
  206. package/index.mjs +0 -60
  207. package/lib/adapters/DslAdapter.js +0 -995
  208. package/lib/adapters/index.js +0 -20
  209. package/lib/config/constants.js +0 -286
  210. package/lib/config/patterns/common.js +0 -47
  211. package/lib/config/patterns/creditCard.js +0 -9
  212. package/lib/config/patterns/idCard.js +0 -9
  213. package/lib/config/patterns/index.js +0 -9
  214. package/lib/config/patterns/licensePlate.js +0 -4
  215. package/lib/config/patterns/passport.js +0 -4
  216. package/lib/config/patterns/phone.js +0 -9
  217. package/lib/config/patterns/postalCode.js +0 -5
  218. package/lib/core/CacheManager.js +0 -376
  219. package/lib/core/ConditionalBuilder.js +0 -503
  220. package/lib/core/DslBuilder.js +0 -1589
  221. package/lib/core/ErrorCodes.js +0 -233
  222. package/lib/core/ErrorFormatter.js +0 -445
  223. package/lib/core/JSONSchemaCore.js +0 -347
  224. package/lib/core/Locale.js +0 -130
  225. package/lib/core/MessageTemplate.js +0 -98
  226. package/lib/core/PluginManager.js +0 -448
  227. package/lib/core/StringExtensions.js +0 -240
  228. package/lib/core/Validator.js +0 -654
  229. package/lib/errors/I18nError.js +0 -328
  230. package/lib/errors/ValidationError.js +0 -191
  231. package/lib/exporters/MarkdownExporter.js +0 -420
  232. package/lib/exporters/MongoDBExporter.js +0 -162
  233. package/lib/exporters/MySQLExporter.js +0 -212
  234. package/lib/exporters/PostgreSQLExporter.js +0 -289
  235. package/lib/exporters/index.js +0 -24
  236. package/lib/locales/index.js +0 -8
  237. package/lib/utils/LRUCache.js +0 -174
  238. package/lib/utils/SchemaHelper.js +0 -240
  239. package/lib/utils/SchemaUtils.js +0 -445
  240. package/lib/utils/TypeConverter.js +0 -245
  241. package/lib/utils/index.js +0 -13
  242. package/lib/validators/CustomKeywords.js +0 -616
  243. package/lib/validators/index.js +0 -11
@@ -1,484 +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, errors: [] },
162
- // { valid: false, errors: [...] },
163
- // { valid: false, 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
- const result = validate(schema, data);
279
- console.log(`验证耗时: ${result.performance?.duration}ms`);
280
- ```
281
-
282
- ---
283
-
284
- ## 常见场景
285
-
286
- ### 用户注册表单
287
-
288
- ```javascript
289
- const registerSchema = dsl({
290
- username: 'string:3-32!'
291
- .pattern(/^[a-zA-Z0-9_]+$/)
292
- .label('用户名')
293
- .messages({
294
- 'pattern': '{{#label}}只能包含字母、数字和下划线'
295
- }),
296
-
297
- email: 'email!'
298
- .label('邮箱地址'),
299
-
300
- password: 'string:8-64!'
301
- .password('strong')
302
- .label('密码'),
303
-
304
- age: 'number:18-120'
305
- .label('年龄'),
306
-
307
- gender: 'male|female|other',
308
-
309
- terms: 'boolean!'
310
- .label('服务条款')
311
- .messages({
312
- 'required': '请同意{{#label}}'
313
- })
314
- });
315
- ```
316
-
317
- ### API 请求验证
318
-
319
- ```javascript
320
- const createOrderSchema = dsl({
321
- userId: 'string!',
322
- items: 'array!1-100',
323
- shippingAddress: {
324
- street: 'string:5-200!',
325
- city: 'string:2-100!',
326
- zipCode: 'string:5-10!',
327
- country: 'string:2!'
328
- },
329
- paymentMethod: 'credit_card|paypal|bank_transfer',
330
- notes: 'string:500'
331
- });
332
-
333
- // Express 中间件
334
- function validateRequest(schema) {
335
- return (req, res, next) => {
336
- const result = validate(schema, req.body);
337
- if (!result.valid) {
338
- return res.status(400).json({ errors: result.errors });
339
- }
340
- req.validatedData = result.data;
341
- next();
342
- };
343
- }
344
-
345
- app.post('/orders', validateRequest(createOrderSchema), createOrder);
346
- ```
347
-
348
- ### 配置文件验证
349
-
350
- ```javascript
351
- const configSchema = dsl({
352
- server: {
353
- host: 'string!',
354
- port: 'integer:1-65535!',
355
- ssl: 'boolean'
356
- },
357
- database: {
358
- url: 'url!',
359
- poolSize: 'integer:1-100',
360
- timeout: 'integer:1000-60000'
361
- },
362
- logging: {
363
- level: 'debug|info|warn|error',
364
- format: 'json|text'
365
- }
366
- });
367
-
368
- function loadConfig(configPath) {
369
- const config = require(configPath);
370
- const result = validate(configSchema, config);
371
-
372
- if (!result.valid) {
373
- throw new Error(`配置文件错误:\n${formatErrors(result.errors)}`);
374
- }
375
-
376
- return result.data;
377
- }
378
- ```
379
-
380
- ---
381
-
382
- ## 最佳实践
383
-
384
- ### 1. 使用 label 提升错误消息质量
385
-
386
- ```javascript
387
- // ❌ 默认错误消息
388
- email: 'email!'
389
- // 错误: "email is required"
390
-
391
- // ✅ 使用 label
392
- email: 'email!'.label('邮箱地址')
393
- // 错误: "邮箱地址不能为空"
394
- ```
395
-
396
- ### 2. 集中管理 Schema
397
-
398
- ```javascript
399
- // schemas/index.js
400
- const { dsl } = require('schema-dsl');
401
-
402
- exports.userSchema = dsl({
403
- username: 'string:3-32!',
404
- email: 'email!'
405
- });
406
-
407
- exports.orderSchema = dsl({
408
- userId: 'string!',
409
- items: 'array!1-100'
410
- });
411
- ```
412
-
413
- ### 3. 使用 SchemaUtils 复用字段
414
-
415
- ```javascript
416
- const { SchemaUtils, dsl } = require('schema-dsl');
417
-
418
- // 创建可复用字段
419
- const emailField = SchemaUtils.reusable(() =>
420
- dsl('email!').label('邮箱地址')
421
- );
422
-
423
- // 在多个 Schema 中复用
424
- const loginSchema = dsl({ email: emailField() });
425
- const registerSchema = dsl({ email: emailField(), name: 'string!' });
426
- ```
427
-
428
- ### 4. 分层验证
429
-
430
- ```javascript
431
- // 基础验证(快速)
432
- const quickSchema = dsl({
433
- username: 'string!',
434
- email: 'string!'
435
- });
436
-
437
- // 完整验证(详细)
438
- const fullSchema = dsl({
439
- username: 'string:3-32!'.pattern(/^[a-z]+$/),
440
- email: 'email!'.custom(async (v) => checkEmailUnique(v))
441
- });
442
-
443
- // 先快速验证,再完整验证
444
- function validateWithFallback(data) {
445
- const quick = validate(quickSchema, data);
446
- if (!quick.valid) return quick;
447
-
448
- return validate(fullSchema, data);
449
- }
450
- ```
451
-
452
- ### 5. 测试验证逻辑
453
-
454
- ```javascript
455
- describe('User Schema', () => {
456
- it('应该验证有效用户', () => {
457
- const result = validate(userSchema, {
458
- username: 'john_doe',
459
- email: 'john@example.com'
460
- });
461
- expect(result.valid).to.be.true;
462
- });
463
-
464
- it('应该拒绝短用户名', () => {
465
- const result = validate(userSchema, {
466
- username: 'ab',
467
- email: 'john@example.com'
468
- });
469
- expect(result.valid).to.be.false;
470
- expect(result.errors[0].keyword).to.equal('minLength');
471
- });
472
- });
473
- ```
474
-
475
- ---
476
-
477
- ## 相关文档
478
-
479
- - [DSL 语法完整指南](dsl-syntax.md)
480
- - [validate 方法详解](validate.md)
481
- - [错误处理指南](error-handling.md)
482
- - [多语言支持](dynamic-locale.md)
483
- - [String 扩展](string-extensions.md)
484
-
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
+