@dangao/bun-server 1.7.0 → 1.8.0
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/README.md +196 -19
- package/dist/cache/cache-module.d.ts +18 -0
- package/dist/cache/cache-module.d.ts.map +1 -1
- package/dist/cache/index.d.ts +3 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/interceptors.d.ts +41 -0
- package/dist/cache/interceptors.d.ts.map +1 -0
- package/dist/cache/service-proxy.d.ts +62 -0
- package/dist/cache/service-proxy.d.ts.map +1 -0
- package/dist/controller/controller.d.ts +8 -0
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts +5 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/di/container.d.ts +18 -1
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +2 -2
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +17 -0
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/di/types.d.ts +22 -0
- package/dist/di/types.d.ts.map +1 -1
- package/dist/events/decorators.d.ts +52 -0
- package/dist/events/decorators.d.ts.map +1 -0
- package/dist/events/event-module.d.ts +97 -0
- package/dist/events/event-module.d.ts.map +1 -0
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/service.d.ts +76 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/types.d.ts +184 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4641 -2840
- package/dist/security/filter.d.ts +23 -0
- package/dist/security/filter.d.ts.map +1 -1
- package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
- package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
- package/dist/security/guards/builtin/index.d.ts +3 -0
- package/dist/security/guards/builtin/index.d.ts.map +1 -0
- package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
- package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
- package/dist/security/guards/decorators.d.ts +50 -0
- package/dist/security/guards/decorators.d.ts.map +1 -0
- package/dist/security/guards/execution-context.d.ts +56 -0
- package/dist/security/guards/execution-context.d.ts.map +1 -0
- package/dist/security/guards/guard-registry.d.ts +67 -0
- package/dist/security/guards/guard-registry.d.ts.map +1 -0
- package/dist/security/guards/index.d.ts +7 -0
- package/dist/security/guards/index.d.ts.map +1 -0
- package/dist/security/guards/reflector.d.ts +57 -0
- package/dist/security/guards/reflector.d.ts.map +1 -0
- package/dist/security/guards/types.d.ts +126 -0
- package/dist/security/guards/types.d.ts.map +1 -0
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/security-module.d.ts +20 -0
- package/dist/security/security-module.d.ts.map +1 -1
- package/dist/validation/class-validator.d.ts +108 -0
- package/dist/validation/class-validator.d.ts.map +1 -0
- package/dist/validation/custom-validator.d.ts +130 -0
- package/dist/validation/custom-validator.d.ts.map +1 -0
- package/dist/validation/errors.d.ts +22 -2
- package/dist/validation/errors.d.ts.map +1 -1
- package/dist/validation/index.d.ts +7 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/rules/array.d.ts +33 -0
- package/dist/validation/rules/array.d.ts.map +1 -0
- package/dist/validation/rules/common.d.ts +90 -0
- package/dist/validation/rules/common.d.ts.map +1 -0
- package/dist/validation/rules/conditional.d.ts +30 -0
- package/dist/validation/rules/conditional.d.ts.map +1 -0
- package/dist/validation/rules/index.d.ts +5 -0
- package/dist/validation/rules/index.d.ts.map +1 -0
- package/dist/validation/rules/object.d.ts +30 -0
- package/dist/validation/rules/object.d.ts.map +1 -0
- package/dist/validation/types.d.ts +52 -1
- package/dist/validation/types.d.ts.map +1 -1
- package/docs/events.md +494 -0
- package/docs/guards.md +376 -0
- package/docs/guide.md +309 -1
- package/docs/request-lifecycle.md +444 -0
- package/docs/symbol-interface-pattern.md +431 -0
- package/docs/validation.md +407 -0
- package/docs/zh/events.md +494 -0
- package/docs/zh/guards.md +376 -0
- package/docs/zh/guide.md +309 -1
- package/docs/zh/request-lifecycle.md +444 -0
- package/docs/zh/symbol-interface-pattern.md +431 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/cache/cache-module.ts +37 -0
- package/src/cache/index.ts +16 -1
- package/src/cache/interceptors.ts +295 -0
- package/src/cache/service-proxy.ts +219 -0
- package/src/controller/controller.ts +30 -6
- package/src/core/application.ts +25 -1
- package/src/di/container.ts +57 -7
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +17 -2
- package/src/di/module-registry.ts +39 -0
- package/src/di/types.ts +29 -0
- package/src/events/decorators.ts +103 -0
- package/src/events/event-module.ts +272 -0
- package/src/events/index.ts +32 -0
- package/src/events/service.ts +352 -0
- package/src/events/types.ts +223 -0
- package/src/index.ts +140 -1
- package/src/security/filter.ts +88 -8
- package/src/security/guards/builtin/auth-guard.ts +68 -0
- package/src/security/guards/builtin/index.ts +3 -0
- package/src/security/guards/builtin/roles-guard.ts +165 -0
- package/src/security/guards/decorators.ts +124 -0
- package/src/security/guards/execution-context.ts +152 -0
- package/src/security/guards/guard-registry.ts +164 -0
- package/src/security/guards/index.ts +7 -0
- package/src/security/guards/reflector.ts +99 -0
- package/src/security/guards/types.ts +144 -0
- package/src/security/index.ts +1 -0
- package/src/security/security-module.ts +72 -2
- package/src/validation/class-validator.ts +322 -0
- package/src/validation/custom-validator.ts +289 -0
- package/src/validation/errors.ts +50 -2
- package/src/validation/index.ts +103 -1
- package/src/validation/rules/array.ts +118 -0
- package/src/validation/rules/common.ts +286 -0
- package/src/validation/rules/conditional.ts +52 -0
- package/src/validation/rules/index.ts +51 -0
- package/src/validation/rules/object.ts +86 -0
- package/src/validation/types.ts +61 -1
- package/tests/cache/cache-decorators.test.ts +284 -0
- package/tests/controller/path-combination.test.ts +353 -0
- package/tests/di/global-module.test.ts +487 -0
- package/tests/events/event-decorators.test.ts +173 -0
- package/tests/events/event-emitter.test.ts +373 -0
- package/tests/events/event-module.test.ts +373 -0
- package/tests/security/guards/guards-integration.test.ts +371 -0
- package/tests/security/guards/guards.test.ts +775 -0
- package/tests/security/security-module.test.ts +2 -2
- package/tests/validation/class-validator.test.ts +349 -0
- package/tests/validation/custom-validator.test.ts +335 -0
- package/tests/validation/rules.test.ts +543 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# 验证系统
|
|
2
|
+
|
|
3
|
+
Bun Server Framework 提供了强大的验证系统,用于验证请求参数、DTO 和复杂数据结构。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- [基础验证](#基础验证)
|
|
8
|
+
- [验证规则](#验证规则)
|
|
9
|
+
- [对象规则](#对象规则)
|
|
10
|
+
- [数组规则](#数组规则)
|
|
11
|
+
- [通用规则](#通用规则)
|
|
12
|
+
- [条件规则](#条件规则)
|
|
13
|
+
- [类级别验证](#类级别验证)
|
|
14
|
+
- [嵌套对象验证](#嵌套对象验证)
|
|
15
|
+
- [自定义验证器](#自定义验证器)
|
|
16
|
+
- [内置扩展验证器](#内置扩展验证器)
|
|
17
|
+
- [错误处理](#错误处理)
|
|
18
|
+
|
|
19
|
+
## 基础验证
|
|
20
|
+
|
|
21
|
+
在控制器方法参数上使用 `@Validate()` 装饰器:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Controller, GET, Query, Validate, IsString, IsEmail, IsOptional, MinLength } from '@dangao/bun-server';
|
|
25
|
+
|
|
26
|
+
@Controller('/api/users')
|
|
27
|
+
class UserController {
|
|
28
|
+
@GET('/search')
|
|
29
|
+
public search(
|
|
30
|
+
@Query('email') @Validate(IsEmail()) email: string,
|
|
31
|
+
@Query('name') @Validate(IsOptional(), IsString(), MinLength(2)) name?: string,
|
|
32
|
+
) {
|
|
33
|
+
return { email, name };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 验证规则
|
|
39
|
+
|
|
40
|
+
### 对象规则
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { IsObject, IsNotEmpty, IsNotEmptyObject, ValidateNested } from '@dangao/bun-server';
|
|
44
|
+
|
|
45
|
+
// 验证值是否为对象
|
|
46
|
+
IsObject()
|
|
47
|
+
|
|
48
|
+
// 验证值是否非空(null、undefined、空字符串、空数组、空对象)
|
|
49
|
+
IsNotEmpty()
|
|
50
|
+
|
|
51
|
+
// 验证值是否为非空对象
|
|
52
|
+
IsNotEmptyObject()
|
|
53
|
+
|
|
54
|
+
// 标记属性需要嵌套验证
|
|
55
|
+
ValidateNested({ each?: boolean }) // each: true 用于数组元素
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 数组规则
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import {
|
|
62
|
+
IsArray, ArrayMinSize, ArrayMaxSize, ArrayUnique,
|
|
63
|
+
ArrayContains, ArrayNotContains, ArrayNotEmpty
|
|
64
|
+
} from '@dangao/bun-server';
|
|
65
|
+
|
|
66
|
+
// 验证值是否为数组
|
|
67
|
+
IsArray()
|
|
68
|
+
|
|
69
|
+
// 验证数组最小长度
|
|
70
|
+
ArrayMinSize(2)
|
|
71
|
+
|
|
72
|
+
// 验证数组最大长度
|
|
73
|
+
ArrayMaxSize(10)
|
|
74
|
+
|
|
75
|
+
// 验证数组元素是否唯一
|
|
76
|
+
ArrayUnique()
|
|
77
|
+
|
|
78
|
+
// 验证数组是否包含指定值
|
|
79
|
+
ArrayContains([1, 2])
|
|
80
|
+
|
|
81
|
+
// 验证数组是否不包含指定值
|
|
82
|
+
ArrayNotContains(['banned'])
|
|
83
|
+
|
|
84
|
+
// 验证数组是否非空
|
|
85
|
+
ArrayNotEmpty()
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 通用规则
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import {
|
|
92
|
+
IsString, IsNumber, IsBoolean, IsInt, IsPositive, IsNegative,
|
|
93
|
+
Min, Max, IsDate, IsUUID, Length, MaxLength, MinLength,
|
|
94
|
+
Matches, IsIn, IsNotIn, IsUrl, IsJSON, IsEmail,
|
|
95
|
+
Equals, NotEquals, IsDefined, IsAlphanumeric, IsAlpha, IsNumberString
|
|
96
|
+
} from '@dangao/bun-server';
|
|
97
|
+
|
|
98
|
+
// 类型验证
|
|
99
|
+
IsString()
|
|
100
|
+
IsNumber()
|
|
101
|
+
IsBoolean()
|
|
102
|
+
IsInt()
|
|
103
|
+
IsDate()
|
|
104
|
+
|
|
105
|
+
// 数字验证
|
|
106
|
+
IsPositive()
|
|
107
|
+
IsNegative()
|
|
108
|
+
Min(0)
|
|
109
|
+
Max(100)
|
|
110
|
+
|
|
111
|
+
// 字符串验证
|
|
112
|
+
IsEmail()
|
|
113
|
+
IsUUID('4') // '3', '4', '5', 或 'all'
|
|
114
|
+
Length(2, 10)
|
|
115
|
+
MinLength(2)
|
|
116
|
+
MaxLength(10)
|
|
117
|
+
Matches(/^[a-z]+$/)
|
|
118
|
+
IsUrl()
|
|
119
|
+
IsJSON()
|
|
120
|
+
IsAlphanumeric()
|
|
121
|
+
IsAlpha()
|
|
122
|
+
IsNumberString()
|
|
123
|
+
|
|
124
|
+
// 值验证
|
|
125
|
+
IsIn(['a', 'b', 'c'])
|
|
126
|
+
IsNotIn(['x', 'y', 'z'])
|
|
127
|
+
Equals('expected')
|
|
128
|
+
NotEquals('forbidden')
|
|
129
|
+
IsDefined()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 条件规则
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { ValidateIf, Transform } from '@dangao/bun-server';
|
|
136
|
+
|
|
137
|
+
// 条件验证 - 仅当条件为 true 时执行验证
|
|
138
|
+
ValidateIf((value, obj) => obj.type === 'premium')
|
|
139
|
+
|
|
140
|
+
// 验证前转换值
|
|
141
|
+
Transform((value) => String(value).trim())
|
|
142
|
+
Transform((value) => Number(value))
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
使用示例:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
@ValidateClass()
|
|
149
|
+
class UpdateUserDto {
|
|
150
|
+
@Property(ValidateIf((_, obj) => obj.type === 'premium'), IsEmail())
|
|
151
|
+
premiumEmail?: string;
|
|
152
|
+
|
|
153
|
+
@Property(Transform((v) => String(v).trim()), IsString(), MinLength(1))
|
|
154
|
+
name: string;
|
|
155
|
+
|
|
156
|
+
@Property(Transform((v) => Number(v)), IsInt(), Min(0))
|
|
157
|
+
age: number;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 类级别验证
|
|
162
|
+
|
|
163
|
+
对于 DTO 类,使用 `@ValidateClass()` 和 `@Property()` 装饰器:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { ValidateClass, Property, IsString, IsEmail, IsOptional, IsInt, Min, Max, MinLength } from '@dangao/bun-server';
|
|
167
|
+
|
|
168
|
+
@ValidateClass()
|
|
169
|
+
class CreateUserDto {
|
|
170
|
+
@Property(IsString(), MinLength(2))
|
|
171
|
+
name: string;
|
|
172
|
+
|
|
173
|
+
@Property(IsEmail())
|
|
174
|
+
email: string;
|
|
175
|
+
|
|
176
|
+
@Property(IsOptional(), IsInt(), Min(0), Max(150))
|
|
177
|
+
age?: number;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
手动验证对象:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { validateObject, validateObjectSync, ValidationError } from '@dangao/bun-server';
|
|
185
|
+
|
|
186
|
+
// 失败时抛出 ValidationError
|
|
187
|
+
try {
|
|
188
|
+
validateObject(data, CreateUserDto);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof ValidationError) {
|
|
191
|
+
console.log(error.issues);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 返回验证结果而不抛出异常
|
|
196
|
+
const result = validateObjectSync(data, CreateUserDto);
|
|
197
|
+
if (!result.valid) {
|
|
198
|
+
console.log(result.issues);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## 嵌套对象验证
|
|
203
|
+
|
|
204
|
+
用于嵌套对象和数组:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { ValidateClass, Property, NestedProperty, ArrayNestedProperty, IsString, IsNumber, Min, IsArray, ArrayMinSize } from '@dangao/bun-server';
|
|
208
|
+
|
|
209
|
+
@ValidateClass()
|
|
210
|
+
class AddressDto {
|
|
211
|
+
@Property(IsString())
|
|
212
|
+
city: string;
|
|
213
|
+
|
|
214
|
+
@Property(IsString())
|
|
215
|
+
street: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
@ValidateClass()
|
|
219
|
+
class ItemDto {
|
|
220
|
+
@Property(IsString())
|
|
221
|
+
name: string;
|
|
222
|
+
|
|
223
|
+
@Property(IsNumber(), Min(0))
|
|
224
|
+
price: number;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@ValidateClass()
|
|
228
|
+
class CreateOrderDto {
|
|
229
|
+
@Property(IsString())
|
|
230
|
+
userId: string;
|
|
231
|
+
|
|
232
|
+
// 嵌套对象验证
|
|
233
|
+
@NestedProperty(AddressDto)
|
|
234
|
+
shippingAddress: AddressDto;
|
|
235
|
+
|
|
236
|
+
// 嵌套对象数组验证
|
|
237
|
+
@Property(IsArray(), ArrayMinSize(1))
|
|
238
|
+
@ArrayNestedProperty(ItemDto)
|
|
239
|
+
items: ItemDto[];
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## 自定义验证器
|
|
244
|
+
|
|
245
|
+
### 简单自定义验证器
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { createSimpleValidator } from '@dangao/bun-server';
|
|
249
|
+
|
|
250
|
+
const IsEven = createSimpleValidator(
|
|
251
|
+
'isEven',
|
|
252
|
+
(value) => typeof value === 'number' && value % 2 === 0,
|
|
253
|
+
'必须是偶数'
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// 使用
|
|
257
|
+
@Property(IsEven())
|
|
258
|
+
count: number;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 带参数的自定义验证器
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { createCustomValidator } from '@dangao/bun-server';
|
|
265
|
+
|
|
266
|
+
const IsDivisibleBy = createCustomValidator(
|
|
267
|
+
'isDivisibleBy',
|
|
268
|
+
(value: unknown, divisor: number) => typeof value === 'number' && value % divisor === 0,
|
|
269
|
+
(divisor: number) => `必须能被 ${divisor} 整除`
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
// 使用
|
|
273
|
+
@Property(IsDivisibleBy(5)())
|
|
274
|
+
value: number;
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 正则表达式自定义验证器
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { createRegexValidator } from '@dangao/bun-server';
|
|
281
|
+
|
|
282
|
+
const IsSlug = createRegexValidator(
|
|
283
|
+
'isSlug',
|
|
284
|
+
/^[a-z0-9]+(?:-[a-z0-9]+)*$/,
|
|
285
|
+
'必须是有效的 slug 格式'
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// 使用
|
|
289
|
+
@Property(IsSlug())
|
|
290
|
+
slug: string;
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## 内置扩展验证器
|
|
294
|
+
|
|
295
|
+
框架提供了多个预置验证器用于常见场景:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import {
|
|
299
|
+
IsPhoneNumber, // 中国手机号
|
|
300
|
+
IsIdCard, // 中国身份证号
|
|
301
|
+
IsIPv4, // IPv4 地址
|
|
302
|
+
IsPort, // 端口号 (0-65535)
|
|
303
|
+
IsPostalCode, // 中国邮政编码
|
|
304
|
+
IsCreditCard, // 信用卡号(Luhn 算法)
|
|
305
|
+
IsHexColor, // 十六进制颜色值(#fff 或 #ffffff)
|
|
306
|
+
IsMacAddress, // MAC 地址
|
|
307
|
+
IsSemVer, // 语义化版本号
|
|
308
|
+
IsDivisibleBy, // 能被指定数字整除
|
|
309
|
+
IsBetween, // 数字在范围内
|
|
310
|
+
Contains, // 字符串包含子串
|
|
311
|
+
NotContains, // 字符串不包含子串
|
|
312
|
+
} from '@dangao/bun-server';
|
|
313
|
+
|
|
314
|
+
// 使用示例
|
|
315
|
+
@Property(IsPhoneNumber())
|
|
316
|
+
phone: string;
|
|
317
|
+
|
|
318
|
+
@Property(IsIPv4())
|
|
319
|
+
ip: string;
|
|
320
|
+
|
|
321
|
+
@Property(IsBetween(1, 100)())
|
|
322
|
+
percentage: number;
|
|
323
|
+
|
|
324
|
+
@Property(Contains('http')())
|
|
325
|
+
url: string;
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## 错误处理
|
|
329
|
+
|
|
330
|
+
### ValidationError
|
|
331
|
+
|
|
332
|
+
验证失败时会抛出 `ValidationError`:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { ValidationError, ValidationIssue } from '@dangao/bun-server';
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
validateObject(data, MyDto);
|
|
339
|
+
} catch (error) {
|
|
340
|
+
if (error instanceof ValidationError) {
|
|
341
|
+
// 访问验证问题列表
|
|
342
|
+
console.log(error.issues);
|
|
343
|
+
|
|
344
|
+
// 获取扁平化的错误(对嵌套对象有用)
|
|
345
|
+
console.log(error.getFlattened());
|
|
346
|
+
|
|
347
|
+
// 转换为 JSON
|
|
348
|
+
console.log(error.toJSON());
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### ValidationIssue 结构
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
interface ValidationIssue {
|
|
357
|
+
index?: number; // 参数索引(用于参数验证)
|
|
358
|
+
property?: string; // 属性路径(如 'user.address.city')
|
|
359
|
+
rule: string; // 失败的规则名称
|
|
360
|
+
message: string; // 错误消息
|
|
361
|
+
value?: unknown; // 验证失败的值
|
|
362
|
+
children?: ValidationIssue[]; // 嵌套错误
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### 控制器集成
|
|
367
|
+
|
|
368
|
+
验证错误会被自动捕获并返回 400 Bad Request 响应:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
@Controller('/api/users')
|
|
372
|
+
class UserController {
|
|
373
|
+
@POST('/')
|
|
374
|
+
public async createUser(@Body() @Validate(IsObject()) body: unknown) {
|
|
375
|
+
const dto = body as CreateUserDto;
|
|
376
|
+
validateObject(dto, CreateUserDto);
|
|
377
|
+
// ... 创建用户
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## 最佳实践
|
|
383
|
+
|
|
384
|
+
1. **复杂验证使用 DTO**:对于具有多个字段的请求体,使用 `@ValidateClass()` 装饰的 DTO。
|
|
385
|
+
|
|
386
|
+
2. **组合规则**:链式组合多个规则以进行全面验证:
|
|
387
|
+
```typescript
|
|
388
|
+
@Property(IsString(), MinLength(2), MaxLength(50), Matches(/^[a-zA-Z\s]+$/))
|
|
389
|
+
name: string;
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
3. **IsOptional() 放在最前**:始终将 `IsOptional()` 放在规则链的开头:
|
|
393
|
+
```typescript
|
|
394
|
+
@Property(IsOptional(), IsString(), MinLength(2))
|
|
395
|
+
nickname?: string;
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
4. **先转换后验证**:使用 `Transform()` 在验证前规范化数据:
|
|
399
|
+
```typescript
|
|
400
|
+
@Property(Transform((v) => String(v).toLowerCase().trim()), IsEmail())
|
|
401
|
+
email: string;
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
5. **自定义错误消息**:提供清晰、用户友好的错误消息:
|
|
405
|
+
```typescript
|
|
406
|
+
IsEmail({ message: '请输入有效的邮箱地址' })
|
|
407
|
+
```
|
package/package.json
CHANGED
|
@@ -8,11 +8,22 @@ import {
|
|
|
8
8
|
type CacheModuleOptions,
|
|
9
9
|
type CacheStore,
|
|
10
10
|
} from './types';
|
|
11
|
+
import { CachePostProcessor } from './service-proxy';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 缓存后处理器 Token
|
|
15
|
+
*/
|
|
16
|
+
export const CACHE_POST_PROCESSOR_TOKEN = Symbol('@dangao/bun-server:cache:post-processor');
|
|
11
17
|
|
|
12
18
|
@Module({
|
|
13
19
|
providers: [],
|
|
14
20
|
})
|
|
15
21
|
export class CacheModule {
|
|
22
|
+
/**
|
|
23
|
+
* 缓存后处理器实例(单例)
|
|
24
|
+
*/
|
|
25
|
+
private static postProcessor: CachePostProcessor | null = null;
|
|
26
|
+
|
|
16
27
|
/**
|
|
17
28
|
* 创建缓存模块
|
|
18
29
|
* @param options - 模块配置
|
|
@@ -35,6 +46,11 @@ export class CacheModule {
|
|
|
35
46
|
keyPrefix: options.keyPrefix,
|
|
36
47
|
});
|
|
37
48
|
|
|
49
|
+
// 创建缓存后处理器(单例)
|
|
50
|
+
if (!CacheModule.postProcessor) {
|
|
51
|
+
CacheModule.postProcessor = new CachePostProcessor();
|
|
52
|
+
}
|
|
53
|
+
|
|
38
54
|
providers.push(
|
|
39
55
|
{
|
|
40
56
|
provide: CACHE_SERVICE_TOKEN,
|
|
@@ -44,6 +60,10 @@ export class CacheModule {
|
|
|
44
60
|
provide: CACHE_OPTIONS_TOKEN,
|
|
45
61
|
useValue: options,
|
|
46
62
|
},
|
|
63
|
+
{
|
|
64
|
+
provide: CACHE_POST_PROCESSOR_TOKEN,
|
|
65
|
+
useValue: CacheModule.postProcessor,
|
|
66
|
+
},
|
|
47
67
|
CacheService,
|
|
48
68
|
);
|
|
49
69
|
|
|
@@ -57,6 +77,7 @@ export class CacheModule {
|
|
|
57
77
|
...(existingMetadata.exports || []),
|
|
58
78
|
CACHE_SERVICE_TOKEN,
|
|
59
79
|
CACHE_OPTIONS_TOKEN,
|
|
80
|
+
CACHE_POST_PROCESSOR_TOKEN,
|
|
60
81
|
CacheService,
|
|
61
82
|
],
|
|
62
83
|
};
|
|
@@ -64,4 +85,20 @@ export class CacheModule {
|
|
|
64
85
|
|
|
65
86
|
return CacheModule;
|
|
66
87
|
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 获取缓存后处理器
|
|
91
|
+
* 用于在应用启动时注册到 DI 容器
|
|
92
|
+
*/
|
|
93
|
+
public static getPostProcessor(): CachePostProcessor | null {
|
|
94
|
+
return CacheModule.postProcessor;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 重置模块状态(主要用于测试)
|
|
99
|
+
*/
|
|
100
|
+
public static reset(): void {
|
|
101
|
+
CacheModule.postProcessor = null;
|
|
102
|
+
Reflect.deleteMetadata(MODULE_METADATA_KEY, CacheModule);
|
|
103
|
+
}
|
|
67
104
|
}
|
package/src/cache/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CacheModule } from './cache-module';
|
|
1
|
+
export { CacheModule, CACHE_POST_PROCESSOR_TOKEN } from './cache-module';
|
|
2
2
|
export { CacheService } from './service';
|
|
3
3
|
export {
|
|
4
4
|
Cacheable,
|
|
@@ -25,3 +25,18 @@ export type {
|
|
|
25
25
|
CacheModuleOptions,
|
|
26
26
|
RedisCacheStoreOptions,
|
|
27
27
|
} from './types';
|
|
28
|
+
export {
|
|
29
|
+
CacheServiceProxy,
|
|
30
|
+
CachePostProcessor,
|
|
31
|
+
EnableCacheProxy,
|
|
32
|
+
isCacheProxyEnabled,
|
|
33
|
+
CACHE_PROXY_ENABLED_KEY,
|
|
34
|
+
} from './service-proxy';
|
|
35
|
+
export {
|
|
36
|
+
CacheableInterceptor,
|
|
37
|
+
CacheEvictInterceptor,
|
|
38
|
+
CachePutInterceptor,
|
|
39
|
+
CACHEABLE_INTERCEPTOR_KEY,
|
|
40
|
+
CACHE_EVICT_INTERCEPTOR_KEY,
|
|
41
|
+
CACHE_PUT_INTERCEPTOR_KEY,
|
|
42
|
+
} from './interceptors';
|