koatty_validation 1.6.0 → 1.6.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.
- package/CHANGELOG.md +2 -0
- package/README.md +106 -3
- package/dist/README.md +106 -3
- package/dist/index.d.ts +66 -52
- package/dist/index.js +133 -104
- package/dist/index.mjs +134 -106
- package/dist/package.json +1 -1
- package/examples/validated-async-sync-example.ts +213 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date: 2025-10-23
|
|
3
|
+
* @Date: 2025-10-23 20:54:39
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
@@ -33,22 +33,22 @@ function _interopNamespaceDefault(e) {
|
|
|
33
33
|
var helper__namespace = /*#__PURE__*/_interopNamespaceDefault(helper);
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* koatty_validation
|
|
36
|
+
* koatty_validation type definitions
|
|
37
37
|
* @author richen
|
|
38
38
|
* @copyright Copyright (c) - <richenlin(at)gmail.com>
|
|
39
39
|
* @license MIT
|
|
40
40
|
*/
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* Parameter type key constant
|
|
43
43
|
*/
|
|
44
44
|
const PARAM_TYPE_KEY = 'PARAM_TYPE_KEY';
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
47
|
+
* Performance cache module - Provides multi-level caching and performance monitoring
|
|
48
48
|
* @author richen
|
|
49
49
|
*/
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* Metadata cache
|
|
52
52
|
*/
|
|
53
53
|
class MetadataCache {
|
|
54
54
|
constructor() {
|
|
@@ -61,7 +61,7 @@ class MetadataCache {
|
|
|
61
61
|
return MetadataCache.instance;
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
64
|
+
* Get metadata cache for a class
|
|
65
65
|
*/
|
|
66
66
|
getClassCache(target) {
|
|
67
67
|
if (!this.cache.has(target)) {
|
|
@@ -70,28 +70,28 @@ class MetadataCache {
|
|
|
70
70
|
return this.cache.get(target);
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Cache metadata
|
|
74
74
|
*/
|
|
75
75
|
setMetadata(target, key, value) {
|
|
76
76
|
const classCache = this.getClassCache(target);
|
|
77
77
|
classCache.set(key, value);
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
80
|
+
* Get cached metadata
|
|
81
81
|
*/
|
|
82
82
|
getMetadata(target, key) {
|
|
83
83
|
const classCache = this.getClassCache(target);
|
|
84
84
|
return classCache.get(key);
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
87
|
+
* Check if metadata is cached
|
|
88
88
|
*/
|
|
89
89
|
hasMetadata(target, key) {
|
|
90
90
|
const classCache = this.getClassCache(target);
|
|
91
91
|
return classCache.has(key);
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
94
|
+
* Clear cache for a specific class
|
|
95
95
|
*/
|
|
96
96
|
clearClassCache(target) {
|
|
97
97
|
if (this.cache.has(target)) {
|
|
@@ -100,7 +100,7 @@ class MetadataCache {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
|
-
*
|
|
103
|
+
* Validation result cache
|
|
104
104
|
*/
|
|
105
105
|
class ValidationCache {
|
|
106
106
|
constructor(options) {
|
|
@@ -108,7 +108,7 @@ class ValidationCache {
|
|
|
108
108
|
this.misses = 0;
|
|
109
109
|
this.cache = new lruCache.LRUCache({
|
|
110
110
|
max: (options === null || options === void 0 ? void 0 : options.max) || 5000,
|
|
111
|
-
ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 10, // 10
|
|
111
|
+
ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 10, // 10 minutes
|
|
112
112
|
allowStale: (options === null || options === void 0 ? void 0 : options.allowStale) || false,
|
|
113
113
|
updateAgeOnGet: (options === null || options === void 0 ? void 0 : options.updateAgeOnGet) || true,
|
|
114
114
|
});
|
|
@@ -226,7 +226,7 @@ class RegexCache {
|
|
|
226
226
|
constructor(options) {
|
|
227
227
|
this.cache = new lruCache.LRUCache({
|
|
228
228
|
max: (options === null || options === void 0 ? void 0 : options.max) || 200,
|
|
229
|
-
ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 30, // 30
|
|
229
|
+
ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 30, // 30 minutes
|
|
230
230
|
allowStale: (options === null || options === void 0 ? void 0 : options.allowStale) || false,
|
|
231
231
|
updateAgeOnGet: (options === null || options === void 0 ? void 0 : options.updateAgeOnGet) || true,
|
|
232
232
|
});
|
|
@@ -410,7 +410,7 @@ function cached(validator, ttl) {
|
|
|
410
410
|
try {
|
|
411
411
|
const result = originalMethod.apply(this, args);
|
|
412
412
|
validationCache.set(validator, value, result, ...additionalArgs);
|
|
413
|
-
//
|
|
413
|
+
// If TTL is specified, set expiration time
|
|
414
414
|
if (ttl && ttl > 0) {
|
|
415
415
|
validationCache.setTTL(validator, value, ttl, ...additionalArgs);
|
|
416
416
|
}
|
|
@@ -1135,24 +1135,24 @@ const FunctionValidator = {
|
|
|
1135
1135
|
};
|
|
1136
1136
|
|
|
1137
1137
|
/**
|
|
1138
|
-
*
|
|
1138
|
+
* Decorator Factory - Eliminate decorator code duplication
|
|
1139
1139
|
* @author richen
|
|
1140
1140
|
*/
|
|
1141
1141
|
/**
|
|
1142
|
-
*
|
|
1143
|
-
* @param options
|
|
1144
|
-
* @returns
|
|
1142
|
+
* Factory function to create validation decorators
|
|
1143
|
+
* @param options Decorator configuration options
|
|
1144
|
+
* @returns Decorator factory function
|
|
1145
1145
|
*/
|
|
1146
1146
|
function createValidationDecorator(options) {
|
|
1147
1147
|
const { name, validator, defaultMessage, requiresValue = false } = options;
|
|
1148
1148
|
return function decoratorFactory(...args) {
|
|
1149
|
-
//
|
|
1149
|
+
// Handle parameters: last parameter is ValidationOptions, previous ones are validator function parameters
|
|
1150
1150
|
const validationOptions = args[args.length - 1];
|
|
1151
1151
|
const validatorArgs = requiresValue ? args.slice(0, -1) : [];
|
|
1152
1152
|
return function propertyDecorator(object, propertyName) {
|
|
1153
|
-
//
|
|
1153
|
+
// Set property as exportable
|
|
1154
1154
|
setExpose(object, propertyName);
|
|
1155
|
-
//
|
|
1155
|
+
// Register validation decorator
|
|
1156
1156
|
classValidator.registerDecorator({
|
|
1157
1157
|
name,
|
|
1158
1158
|
target: object.constructor,
|
|
@@ -1180,11 +1180,11 @@ function createValidationDecorator(options) {
|
|
|
1180
1180
|
};
|
|
1181
1181
|
}
|
|
1182
1182
|
/**
|
|
1183
|
-
*
|
|
1184
|
-
* @param name
|
|
1185
|
-
* @param validator
|
|
1186
|
-
* @param defaultMessage
|
|
1187
|
-
* @returns
|
|
1183
|
+
* Create simple validation decorator (no additional parameters required)
|
|
1184
|
+
* @param name Decorator name
|
|
1185
|
+
* @param validator Validation function
|
|
1186
|
+
* @param defaultMessage Default error message
|
|
1187
|
+
* @returns Decorator function
|
|
1188
1188
|
*/
|
|
1189
1189
|
function createSimpleDecorator(name, validator, defaultMessage) {
|
|
1190
1190
|
return createValidationDecorator({
|
|
@@ -1195,11 +1195,11 @@ function createSimpleDecorator(name, validator, defaultMessage) {
|
|
|
1195
1195
|
});
|
|
1196
1196
|
}
|
|
1197
1197
|
/**
|
|
1198
|
-
*
|
|
1199
|
-
* @param name
|
|
1200
|
-
* @param validator
|
|
1201
|
-
* @param defaultMessage
|
|
1202
|
-
* @returns
|
|
1198
|
+
* Create parameterized validation decorator
|
|
1199
|
+
* @param name Decorator name
|
|
1200
|
+
* @param validator Validation function
|
|
1201
|
+
* @param defaultMessage Default error message
|
|
1202
|
+
* @returns Decorator factory function
|
|
1203
1203
|
*/
|
|
1204
1204
|
function createParameterizedDecorator(name, validator, defaultMessage) {
|
|
1205
1205
|
return createValidationDecorator({
|
|
@@ -1211,21 +1211,21 @@ function createParameterizedDecorator(name, validator, defaultMessage) {
|
|
|
1211
1211
|
}
|
|
1212
1212
|
|
|
1213
1213
|
/**
|
|
1214
|
-
*
|
|
1214
|
+
* Improved error handling mechanism
|
|
1215
1215
|
* @author richen
|
|
1216
1216
|
*/
|
|
1217
1217
|
/**
|
|
1218
|
-
*
|
|
1218
|
+
* Error message internationalization
|
|
1219
1219
|
*/
|
|
1220
1220
|
const ERROR_MESSAGES = {
|
|
1221
1221
|
zh: {
|
|
1222
|
-
//
|
|
1222
|
+
// Chinese localization validation
|
|
1223
1223
|
IsCnName: '必须是有效的中文姓名',
|
|
1224
1224
|
IsIdNumber: '必须是有效的身份证号码',
|
|
1225
1225
|
IsZipCode: '必须是有效的邮政编码',
|
|
1226
1226
|
IsMobile: '必须是有效的手机号码',
|
|
1227
1227
|
IsPlateNumber: '必须是有效的车牌号码',
|
|
1228
|
-
//
|
|
1228
|
+
// Basic validation
|
|
1229
1229
|
IsNotEmpty: '不能为空',
|
|
1230
1230
|
IsDate: '必须是有效的日期',
|
|
1231
1231
|
IsEmail: '必须是有效的邮箱地址',
|
|
@@ -1233,7 +1233,7 @@ const ERROR_MESSAGES = {
|
|
|
1233
1233
|
IsPhoneNumber: '必须是有效的电话号码',
|
|
1234
1234
|
IsUrl: '必须是有效的URL地址',
|
|
1235
1235
|
IsHash: '必须是有效的哈希值',
|
|
1236
|
-
//
|
|
1236
|
+
// Comparison validation
|
|
1237
1237
|
Equals: '必须等于 {comparison}',
|
|
1238
1238
|
NotEquals: '不能等于 {comparison}',
|
|
1239
1239
|
Contains: '必须包含 {seed}',
|
|
@@ -1243,7 +1243,7 @@ const ERROR_MESSAGES = {
|
|
|
1243
1243
|
Gte: '必须大于或等于 {min}',
|
|
1244
1244
|
Lt: '必须小于 {max}',
|
|
1245
1245
|
Lte: '必须小于或等于 {max}',
|
|
1246
|
-
//
|
|
1246
|
+
// Common errors
|
|
1247
1247
|
invalidParameter: '参数 {field} 无效',
|
|
1248
1248
|
validationFailed: '验证失败',
|
|
1249
1249
|
},
|
|
@@ -1278,7 +1278,7 @@ const ERROR_MESSAGES = {
|
|
|
1278
1278
|
}
|
|
1279
1279
|
};
|
|
1280
1280
|
/**
|
|
1281
|
-
*
|
|
1281
|
+
* Enhanced validation error class
|
|
1282
1282
|
*/
|
|
1283
1283
|
class KoattyValidationError extends Error {
|
|
1284
1284
|
constructor(errors, message) {
|
|
@@ -1288,23 +1288,23 @@ class KoattyValidationError extends Error {
|
|
|
1288
1288
|
this.errors = errors;
|
|
1289
1289
|
this.statusCode = 400;
|
|
1290
1290
|
this.timestamp = new Date();
|
|
1291
|
-
//
|
|
1291
|
+
// Ensure correct prototype chain
|
|
1292
1292
|
Object.setPrototypeOf(this, KoattyValidationError.prototype);
|
|
1293
1293
|
}
|
|
1294
1294
|
/**
|
|
1295
|
-
*
|
|
1295
|
+
* Get the first error message
|
|
1296
1296
|
*/
|
|
1297
1297
|
getFirstError() {
|
|
1298
1298
|
return this.errors[0];
|
|
1299
1299
|
}
|
|
1300
1300
|
/**
|
|
1301
|
-
*
|
|
1301
|
+
* Get errors for a specific field
|
|
1302
1302
|
*/
|
|
1303
1303
|
getFieldErrors(field) {
|
|
1304
1304
|
return this.errors.filter(error => error.field === field);
|
|
1305
1305
|
}
|
|
1306
1306
|
/**
|
|
1307
|
-
*
|
|
1307
|
+
* Convert to JSON format
|
|
1308
1308
|
*/
|
|
1309
1309
|
toJSON() {
|
|
1310
1310
|
return {
|
|
@@ -1317,7 +1317,7 @@ class KoattyValidationError extends Error {
|
|
|
1317
1317
|
}
|
|
1318
1318
|
}
|
|
1319
1319
|
/**
|
|
1320
|
-
*
|
|
1320
|
+
* Error message formatter
|
|
1321
1321
|
*/
|
|
1322
1322
|
class ErrorMessageFormatter {
|
|
1323
1323
|
constructor(language = 'zh') {
|
|
@@ -1325,33 +1325,33 @@ class ErrorMessageFormatter {
|
|
|
1325
1325
|
this.language = language;
|
|
1326
1326
|
}
|
|
1327
1327
|
/**
|
|
1328
|
-
*
|
|
1328
|
+
* Set language
|
|
1329
1329
|
*/
|
|
1330
1330
|
setLanguage(language) {
|
|
1331
1331
|
this.language = language;
|
|
1332
1332
|
}
|
|
1333
1333
|
/**
|
|
1334
|
-
*
|
|
1334
|
+
* Format error message
|
|
1335
1335
|
*/
|
|
1336
1336
|
formatMessage(constraint, field, value, context) {
|
|
1337
1337
|
const messages = ERROR_MESSAGES[this.language];
|
|
1338
1338
|
let template = messages[constraint] || messages.invalidParameter;
|
|
1339
|
-
//
|
|
1339
|
+
// Replace placeholders
|
|
1340
1340
|
template = template.replace('{field}', field);
|
|
1341
|
-
//
|
|
1341
|
+
// Prioritize values from context, then use passed value
|
|
1342
1342
|
if (context) {
|
|
1343
1343
|
Object.entries(context).forEach(([key, val]) => {
|
|
1344
1344
|
template = template.replace(`{${key}}`, this.formatValue(val));
|
|
1345
1345
|
});
|
|
1346
1346
|
}
|
|
1347
|
-
//
|
|
1347
|
+
// If there's still a {value} placeholder and value was passed, replace it
|
|
1348
1348
|
if (value !== undefined && template.includes('{value}')) {
|
|
1349
1349
|
template = template.replace('{value}', this.formatValue(value));
|
|
1350
1350
|
}
|
|
1351
1351
|
return template;
|
|
1352
1352
|
}
|
|
1353
1353
|
/**
|
|
1354
|
-
*
|
|
1354
|
+
* Format value for message display
|
|
1355
1355
|
* @private
|
|
1356
1356
|
*/
|
|
1357
1357
|
formatValue(value) {
|
|
@@ -1370,7 +1370,7 @@ class ErrorMessageFormatter {
|
|
|
1370
1370
|
return JSON.stringify(value);
|
|
1371
1371
|
}
|
|
1372
1372
|
catch {
|
|
1373
|
-
//
|
|
1373
|
+
// Handle circular references
|
|
1374
1374
|
return '[Circular Reference]';
|
|
1375
1375
|
}
|
|
1376
1376
|
}
|
|
@@ -1378,17 +1378,17 @@ class ErrorMessageFormatter {
|
|
|
1378
1378
|
}
|
|
1379
1379
|
}
|
|
1380
1380
|
/**
|
|
1381
|
-
*
|
|
1381
|
+
* Global error message formatter instance
|
|
1382
1382
|
*/
|
|
1383
1383
|
const errorFormatter = new ErrorMessageFormatter();
|
|
1384
1384
|
/**
|
|
1385
|
-
*
|
|
1385
|
+
* Set global language
|
|
1386
1386
|
*/
|
|
1387
1387
|
function setValidationLanguage(language) {
|
|
1388
1388
|
errorFormatter.setLanguage(language);
|
|
1389
1389
|
}
|
|
1390
1390
|
/**
|
|
1391
|
-
*
|
|
1391
|
+
* Create validation error
|
|
1392
1392
|
*/
|
|
1393
1393
|
function createValidationError(field, value, constraint, customMessage, context) {
|
|
1394
1394
|
const message = customMessage || errorFormatter.formatMessage(constraint, field, value, context);
|
|
@@ -1401,7 +1401,7 @@ function createValidationError(field, value, constraint, customMessage, context)
|
|
|
1401
1401
|
};
|
|
1402
1402
|
}
|
|
1403
1403
|
/**
|
|
1404
|
-
*
|
|
1404
|
+
* Create validation errors in batch
|
|
1405
1405
|
*/
|
|
1406
1406
|
function createValidationErrors(errors) {
|
|
1407
1407
|
const validationErrors = errors.map(error => createValidationError(error.field, error.value, error.constraint, error.message, error.context));
|
|
@@ -1409,19 +1409,19 @@ function createValidationErrors(errors) {
|
|
|
1409
1409
|
}
|
|
1410
1410
|
|
|
1411
1411
|
/**
|
|
1412
|
-
*
|
|
1412
|
+
* Refactored decorator definitions - Using factory functions to eliminate duplication
|
|
1413
1413
|
* @author richen
|
|
1414
1414
|
*/
|
|
1415
|
-
//
|
|
1415
|
+
// Chinese localization validation decorators
|
|
1416
1416
|
const IsCnName = createSimpleDecorator('IsCnName', (value) => helper__namespace.isString(value) && cnName(value), 'must be a valid Chinese name');
|
|
1417
1417
|
const IsIdNumber = createSimpleDecorator('IsIdNumber', (value) => helper__namespace.isString(value) && idNumber(value), 'must be a valid ID number');
|
|
1418
1418
|
const IsZipCode = createSimpleDecorator('IsZipCode', (value) => helper__namespace.isString(value) && zipCode(value), 'must be a valid zip code');
|
|
1419
1419
|
const IsMobile = createSimpleDecorator('IsMobile', (value) => helper__namespace.isString(value) && mobile(value), 'must be a valid mobile number');
|
|
1420
1420
|
const IsPlateNumber = createSimpleDecorator('IsPlateNumber', (value) => helper__namespace.isString(value) && plateNumber(value), 'must be a valid plate number');
|
|
1421
|
-
//
|
|
1421
|
+
// Basic validation decorators
|
|
1422
1422
|
const IsNotEmpty = createSimpleDecorator('IsNotEmpty', (value) => !helper__namespace.isEmpty(value), 'should not be empty');
|
|
1423
1423
|
const IsDate = createSimpleDecorator('IsDate', (value) => helper__namespace.isDate(value), 'must be a valid date');
|
|
1424
|
-
//
|
|
1424
|
+
// Parameterized validation decorators
|
|
1425
1425
|
const Equals = createParameterizedDecorator('Equals', (value, comparison) => value === comparison, 'must equal to $constraint1');
|
|
1426
1426
|
const NotEquals = createParameterizedDecorator('NotEquals', (value, comparison) => value !== comparison, 'should not equal to $constraint1');
|
|
1427
1427
|
const Contains = createParameterizedDecorator('Contains', (value, seed) => helper__namespace.isString(value) && value.includes(seed), 'must contain $constraint1');
|
|
@@ -1448,9 +1448,9 @@ function IsUrl(options, validationOptions) {
|
|
|
1448
1448
|
function IsHash(algorithm, validationOptions) {
|
|
1449
1449
|
return createParameterizedDecorator('IsHash', (value) => classValidator.isHash(value, algorithm), 'must be a valid hash')(validationOptions);
|
|
1450
1450
|
}
|
|
1451
|
-
//
|
|
1451
|
+
// Basic utility decorators (migrated from original decorator.ts)
|
|
1452
1452
|
/**
|
|
1453
|
-
*
|
|
1453
|
+
* Mark property as exportable
|
|
1454
1454
|
*/
|
|
1455
1455
|
function Expose() {
|
|
1456
1456
|
return function (object, propertyName) {
|
|
@@ -1458,7 +1458,7 @@ function Expose() {
|
|
|
1458
1458
|
};
|
|
1459
1459
|
}
|
|
1460
1460
|
/**
|
|
1461
|
-
* Expose
|
|
1461
|
+
* Alias for Expose
|
|
1462
1462
|
*/
|
|
1463
1463
|
function IsDefined() {
|
|
1464
1464
|
return function (object, propertyName) {
|
|
@@ -1466,61 +1466,89 @@ function IsDefined() {
|
|
|
1466
1466
|
};
|
|
1467
1467
|
}
|
|
1468
1468
|
/**
|
|
1469
|
-
*
|
|
1469
|
+
* Parameter validation decorator
|
|
1470
1470
|
*/
|
|
1471
1471
|
function Valid(rule, options) {
|
|
1472
1472
|
return function (object, propertyName, parameterIndex) {
|
|
1473
|
-
//
|
|
1473
|
+
// Keep consistent with original implementation
|
|
1474
1474
|
const existingRules = Reflect.getOwnMetadata("validate", object, propertyName) || {};
|
|
1475
1475
|
existingRules[parameterIndex] = { rule, options };
|
|
1476
1476
|
Reflect.defineMetadata("validate", existingRules, object, propertyName);
|
|
1477
1477
|
};
|
|
1478
1478
|
}
|
|
1479
1479
|
/**
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1480
|
+
* Synchronous validation function - Executes the actual validation logic
|
|
1481
|
+
* @param args Method parameters
|
|
1482
|
+
* @param paramTypes Parameter type metadata
|
|
1483
|
+
* @returns Validated parameters and validation targets
|
|
1482
1484
|
*/
|
|
1483
|
-
function
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
constraint: Object.keys(e.constraints || {})[0] || 'unknown',
|
|
1510
|
-
message: Object.values(e.constraints || {})[0] || 'Validation failed',
|
|
1511
|
-
context: e.constraints
|
|
1512
|
-
})));
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
catch (error) {
|
|
1516
|
-
// 如果验证失败,重新抛出错误
|
|
1517
|
-
throw error;
|
|
1518
|
-
}
|
|
1485
|
+
async function checkValidated(args, paramTypes) {
|
|
1486
|
+
const validationTargets = [];
|
|
1487
|
+
// Validate each parameter
|
|
1488
|
+
for (let i = 0; i < args.length; i++) {
|
|
1489
|
+
const arg = args[i];
|
|
1490
|
+
const paramType = paramTypes[i];
|
|
1491
|
+
// If it's a class type and not a basic type, perform validation
|
|
1492
|
+
if (paramType && typeof paramType === 'function' &&
|
|
1493
|
+
paramType !== String && paramType !== Number &&
|
|
1494
|
+
paramType !== Boolean && paramType !== Array &&
|
|
1495
|
+
paramType !== Object && paramType !== Date) {
|
|
1496
|
+
try {
|
|
1497
|
+
// If parameter is not an instance of the target type, convert it to an instance
|
|
1498
|
+
let validationTarget = arg;
|
|
1499
|
+
if (!(arg instanceof paramType)) {
|
|
1500
|
+
validationTarget = Object.assign(new paramType(), arg);
|
|
1501
|
+
}
|
|
1502
|
+
const errors = await classValidator.validate(validationTarget);
|
|
1503
|
+
if (errors.length > 0) {
|
|
1504
|
+
throw createValidationErrors(errors.map(e => ({
|
|
1505
|
+
field: e.property,
|
|
1506
|
+
value: e.value,
|
|
1507
|
+
constraint: Object.keys(e.constraints || {})[0] || 'unknown',
|
|
1508
|
+
message: Object.values(e.constraints || {})[0] || 'Validation failed',
|
|
1509
|
+
context: e.constraints
|
|
1510
|
+
})));
|
|
1519
1511
|
}
|
|
1512
|
+
validationTargets.push(validationTarget);
|
|
1520
1513
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1514
|
+
catch (error) {
|
|
1515
|
+
// If validation fails, rethrow the error
|
|
1516
|
+
throw error;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
else {
|
|
1520
|
+
validationTargets.push(arg);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
return { validatedArgs: args, validationTargets };
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Method validation decorator
|
|
1527
|
+
* Automatically validates DTO objects in method parameters
|
|
1528
|
+
* @param isAsync Whether to use async validation mode, default is true
|
|
1529
|
+
* - true: Async mode, validation is handled by IOC container in the framework (suitable for scenarios where parameter values need to be obtained asynchronously)
|
|
1530
|
+
* - false: Sync mode, validation is performed immediately when the method is called (suitable for scenarios where parameter values are already prepared)
|
|
1531
|
+
*/
|
|
1532
|
+
function Validated(isAsync = true) {
|
|
1533
|
+
return function (target, propertyKey, descriptor) {
|
|
1534
|
+
if (isAsync) {
|
|
1535
|
+
// Async mode: Save metadata, validation will be performed by the framework after async parameter retrieval
|
|
1536
|
+
koatty_container.IOCContainer.savePropertyData(PARAM_CHECK_KEY, {
|
|
1537
|
+
dtoCheck: 1
|
|
1538
|
+
}, target, propertyKey);
|
|
1539
|
+
}
|
|
1540
|
+
else {
|
|
1541
|
+
// Sync mode: Perform validation immediately when the method is called
|
|
1542
|
+
const originalMethod = descriptor.value;
|
|
1543
|
+
descriptor.value = async function (...args) {
|
|
1544
|
+
// Get parameter type metadata
|
|
1545
|
+
const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyKey) || [];
|
|
1546
|
+
// Execute validation
|
|
1547
|
+
await checkValidated(args, paramTypes);
|
|
1548
|
+
// Execute original method
|
|
1549
|
+
return originalMethod.apply(this, args);
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1524
1552
|
return descriptor;
|
|
1525
1553
|
};
|
|
1526
1554
|
}
|
|
@@ -1562,6 +1590,7 @@ exports.ValidFuncs = ValidFuncs;
|
|
|
1562
1590
|
exports.Validated = Validated;
|
|
1563
1591
|
exports.cached = cached;
|
|
1564
1592
|
exports.checkParamsType = checkParamsType;
|
|
1593
|
+
exports.checkValidated = checkValidated;
|
|
1565
1594
|
exports.clearAllCaches = clearAllCaches;
|
|
1566
1595
|
exports.configureCaches = configureCaches;
|
|
1567
1596
|
exports.convertDtoParamsType = convertDtoParamsType;
|