CheeseAPI 1.2.2__tar.gz → 1.3.0__tar.gz
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.
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/__init__.py +1 -1
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/handle.py +24 -21
- cheeseapi-1.3.0/CheeseAPI/validator.py +91 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/PKG-INFO +3 -1
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/pyproject.toml +4 -2
- cheeseapi-1.2.2/CheeseAPI/validator.py +0 -368
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/.gitignore +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/app.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/cors.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/exception.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/file.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/protocol.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/request.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/response.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/route.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/schedule.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/server.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/signal.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/text.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/websocket.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/CheeseAPI/workspace.py +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/LICENSE +0 -0
- {cheeseapi-1.2.2 → cheeseapi-1.3.0}/README.md +0 -0
|
@@ -5,4 +5,4 @@ from CheeseAPI.request import Request
|
|
|
5
5
|
from CheeseAPI.response import Response, JsonResponse, FileResponse, BaseResponse, RedirectResponse
|
|
6
6
|
from CheeseAPI.route import Route
|
|
7
7
|
from CheeseAPI.websocket import WebsocketServer
|
|
8
|
-
from CheeseAPI.validator import
|
|
8
|
+
from CheeseAPI.validator import validator, ValidateError
|
|
@@ -480,7 +480,7 @@ class Handle:
|
|
|
480
480
|
try:
|
|
481
481
|
Server, kwargs = self._app.routeBus._match(protocol.request.path, 'WEBSOCKET')
|
|
482
482
|
protocol.kwargs.update(kwargs)
|
|
483
|
-
except
|
|
483
|
+
except Route_404_Exception as e:
|
|
484
484
|
await self.websocket_afterRequest(protocol)
|
|
485
485
|
if self._app.signal.websocket_afterRequest.receivers:
|
|
486
486
|
await self._app.signal.websocket_afterRequest.async_send(**{
|
|
@@ -488,27 +488,30 @@ class Handle:
|
|
|
488
488
|
**protocol.kwargs
|
|
489
489
|
})
|
|
490
490
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
await self.
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
'response': protocol.response,
|
|
507
|
-
**protocol.kwargs
|
|
508
|
-
})
|
|
509
|
-
return await self.websocket_response(protocol)
|
|
491
|
+
await self.websocket_404(protocol)
|
|
492
|
+
if self._app.signal.websocket_404.receivers:
|
|
493
|
+
await self._app.signal.websocket_404.async_send(**{
|
|
494
|
+
'request': protocol.request,
|
|
495
|
+
'response': protocol.response,
|
|
496
|
+
**protocol.kwargs
|
|
497
|
+
})
|
|
498
|
+
return await self.websocket_response(protocol)
|
|
499
|
+
except Route_405_Exception as e:
|
|
500
|
+
await self.websocket_afterRequest(protocol)
|
|
501
|
+
if self._app.signal.websocket_afterRequest.receivers:
|
|
502
|
+
await self._app.signal.websocket_afterRequest.async_send(**{
|
|
503
|
+
'request': protocol.request,
|
|
504
|
+
**protocol.kwargs
|
|
505
|
+
})
|
|
510
506
|
|
|
511
|
-
|
|
507
|
+
await self.websocket_405(protocol)
|
|
508
|
+
if self._app.signal.websocket_405.receivers:
|
|
509
|
+
await self._app.signal.websocket_405.async_send(**{
|
|
510
|
+
'request': protocol.request,
|
|
511
|
+
'response': protocol.response,
|
|
512
|
+
**protocol.kwargs
|
|
513
|
+
})
|
|
514
|
+
return await self.websocket_response(protocol)
|
|
512
515
|
|
|
513
516
|
protocol.server = Server()
|
|
514
517
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
from functools import wraps
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ValidationError
|
|
6
|
+
|
|
7
|
+
from CheeseAPI.response import JsonResponse
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from CheeseAPI.request import Request
|
|
11
|
+
from CheeseAPI.response import BaseResponse
|
|
12
|
+
|
|
13
|
+
class ValidateError(Exception):
|
|
14
|
+
def __init__(self, response: 'BaseResponse'):
|
|
15
|
+
self.response: 'BaseResponse' = response
|
|
16
|
+
|
|
17
|
+
def validator(validator: BaseModel):
|
|
18
|
+
'''
|
|
19
|
+
为路由函数添加校验装饰器。
|
|
20
|
+
|
|
21
|
+
校验参数以类的校验属性为key,从路径变量、args、form、cookie、headers按顺序尝试匹配,若全部匹配失败,则会默认为None。
|
|
22
|
+
|
|
23
|
+
校验通过后,路由函数会收到一个`validator: BaseModel`已校验参数。
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from CheeseAPI import app, validator
|
|
27
|
+
from pydantic import BaseModel, EmailStr, PastDatetime
|
|
28
|
+
|
|
29
|
+
class User(BaseModel):
|
|
30
|
+
mail: EmailStr
|
|
31
|
+
name: str
|
|
32
|
+
birthDate: PastDatetime
|
|
33
|
+
|
|
34
|
+
@app.route.get('/')
|
|
35
|
+
@validator(Form)
|
|
36
|
+
async def test(*, validator: User, **kwargs):
|
|
37
|
+
...
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
在自定义校验中,可以自定义校验失败后返回的Response:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from CheeseAPI import ValidateError, Response
|
|
44
|
+
from pydantic import BaseModel, field_validator
|
|
45
|
+
|
|
46
|
+
class Form(BaseModel):
|
|
47
|
+
value: str
|
|
48
|
+
|
|
49
|
+
@field_validator('value')
|
|
50
|
+
def value(cls, value: str) -> str:
|
|
51
|
+
if ...:
|
|
52
|
+
raise ValidateError(Response('My Response', 400))
|
|
53
|
+
|
|
54
|
+
...
|
|
55
|
+
```
|
|
56
|
+
'''
|
|
57
|
+
|
|
58
|
+
def wrapper(fn):
|
|
59
|
+
@wraps(fn)
|
|
60
|
+
async def decorator(*args, **kwargs):
|
|
61
|
+
request: 'Request' = kwargs['request']
|
|
62
|
+
|
|
63
|
+
_kwargs = {}
|
|
64
|
+
for key in validator.model_fields.keys():
|
|
65
|
+
_kwargs[key] = None
|
|
66
|
+
|
|
67
|
+
for scope in [ 'path', 'args', 'form', 'cookie', 'headers' ]:
|
|
68
|
+
try:
|
|
69
|
+
if scope == 'path':
|
|
70
|
+
_kwargs[key] = kwargs.get(key)
|
|
71
|
+
else:
|
|
72
|
+
_kwargs[key] = getattr(request, scope).get(key)
|
|
73
|
+
|
|
74
|
+
if _kwargs.get(key):
|
|
75
|
+
break
|
|
76
|
+
except:
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
_validator = validator(**_kwargs)
|
|
81
|
+
except ValidationError as e:
|
|
82
|
+
return JsonResponse(json.loads(e.json()), 400)
|
|
83
|
+
except ValidateError as e:
|
|
84
|
+
return e.response
|
|
85
|
+
|
|
86
|
+
return await fn(*args, **{
|
|
87
|
+
**kwargs,
|
|
88
|
+
'validator': _validator
|
|
89
|
+
})
|
|
90
|
+
return decorator
|
|
91
|
+
return wrapper
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: CheeseAPI
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: 一款web协程框架。
|
|
5
5
|
Project-URL: Source, https://github.com/CheeseUnknown/CheeseAPI
|
|
6
6
|
Author-email: Cheese Unknown <cheese@cheese.ren>
|
|
@@ -9,7 +9,9 @@ Keywords: API,BackEnd
|
|
|
9
9
|
Requires-Dist: cheeselog==1.0.*
|
|
10
10
|
Requires-Dist: cheesesignal==1.1.*
|
|
11
11
|
Requires-Dist: dill
|
|
12
|
+
Requires-Dist: email-validator
|
|
12
13
|
Requires-Dist: httptools
|
|
14
|
+
Requires-Dist: pydantic
|
|
13
15
|
Requires-Dist: uvloop
|
|
14
16
|
Requires-Dist: websockets
|
|
15
17
|
Requires-Dist: xmltodict
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "CheeseAPI"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.3.0"
|
|
8
8
|
description = "一款web协程框架。"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license-files = { paths = [ "LICENSE" ] }
|
|
@@ -20,7 +20,9 @@ dependencies = [
|
|
|
20
20
|
"uvloop",
|
|
21
21
|
"httptools",
|
|
22
22
|
"CheeseSignal==1.1.*",
|
|
23
|
-
"dill"
|
|
23
|
+
"dill",
|
|
24
|
+
"pydantic",
|
|
25
|
+
"email-validator"
|
|
24
26
|
]
|
|
25
27
|
|
|
26
28
|
[project.urls]
|
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from typing import List, Literal, Any, Callable, Dict, TYPE_CHECKING
|
|
3
|
-
from functools import wraps
|
|
4
|
-
|
|
5
|
-
from CheeseAPI.response import Response, BaseResponse
|
|
6
|
-
from CheeseAPI.request import Request
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from CheeseAPI.app import App
|
|
10
|
-
|
|
11
|
-
class ValidateError(Exception):
|
|
12
|
-
def __init__(self, response: BaseResponse | None = None):
|
|
13
|
-
'''
|
|
14
|
-
在自定义校验函数中抛出此错误,可结束校验并直接返回响应体。
|
|
15
|
-
'''
|
|
16
|
-
|
|
17
|
-
self.response: BaseResponse | None = response
|
|
18
|
-
|
|
19
|
-
class Bool:
|
|
20
|
-
'''
|
|
21
|
-
将满足小写后等于true或false的字符串转为`bool`。
|
|
22
|
-
'''
|
|
23
|
-
|
|
24
|
-
def __new__(cls, value: str) -> bool:
|
|
25
|
-
if not isinstance(value, str):
|
|
26
|
-
raise ValueError('参数类型错误')
|
|
27
|
-
|
|
28
|
-
if value.lower() == 'true':
|
|
29
|
-
return True
|
|
30
|
-
|
|
31
|
-
if value.lower() == 'false':
|
|
32
|
-
return False
|
|
33
|
-
|
|
34
|
-
raise ValueError('格式错误')
|
|
35
|
-
|
|
36
|
-
class Validator:
|
|
37
|
-
def __init__(self,
|
|
38
|
-
scope: Literal['form', 'headers', 'args', 'path', 'cookie'],
|
|
39
|
-
key: str,
|
|
40
|
-
*,
|
|
41
|
-
required: bool = False,
|
|
42
|
-
default: Any = None,
|
|
43
|
-
type: List[object | Callable] | object | Callable = None,
|
|
44
|
-
expected_type: str | None = None,
|
|
45
|
-
pattern: str | None = None,
|
|
46
|
-
min: object | None = None,
|
|
47
|
-
max: object | None = None,
|
|
48
|
-
enum: List[Any] = [],
|
|
49
|
-
fn: Callable | None = None,
|
|
50
|
-
response: Response | None = None
|
|
51
|
-
):
|
|
52
|
-
'''
|
|
53
|
-
可能的示例:
|
|
54
|
-
|
|
55
|
-
```python
|
|
56
|
-
import datetime
|
|
57
|
-
|
|
58
|
-
from CheeseAPI import Validator
|
|
59
|
-
|
|
60
|
-
Validator('form', 'date', type = [ float, datetime.datetime.fromtimestamp ], expected_type = 'timestamp')
|
|
61
|
-
|
|
62
|
-
Validator('form', 'gender', enum = [ 'man', 'woman', None ])
|
|
63
|
-
|
|
64
|
-
Validator('form', 'idCard', pattern = r'^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[1-2]\\d|3[0-1])\\d{3}[\\dXx]$', response = Response('身份证格式错误', 400))
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
- Args
|
|
68
|
-
|
|
69
|
-
- scope: 域范围。form: request.form;headers: request.headers;args: request.args;path: request.path中的动态路由;cookie: request.cookie。
|
|
70
|
-
|
|
71
|
-
- type: 遵循`self.type(value) -> object`的类或函数;支持list按顺序转换类型。
|
|
72
|
-
|
|
73
|
-
- expected_type: 期望的数据类型,不参与校验,仅供提示。
|
|
74
|
-
|
|
75
|
-
- default: 默认值;该值仍会执行校验流程,而不是直接应用。
|
|
76
|
-
|
|
77
|
-
- pattern: 对于`str`类型的数据,使用正则表达式进行验证。
|
|
78
|
-
|
|
79
|
-
- enum: 指定某些特定的值。
|
|
80
|
-
|
|
81
|
-
- fn: 【更多内容查看完整注释】 自定义校验函数;该函数将在所有预设校验完成后执行。
|
|
82
|
-
|
|
83
|
-
该函数结构应为:
|
|
84
|
-
|
|
85
|
-
```python
|
|
86
|
-
from typing import Dict, Any
|
|
87
|
-
|
|
88
|
-
async def fn(*args, validatedForm: Dict[str, Any], **kwargs) -> Any:
|
|
89
|
-
...
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
参数:
|
|
93
|
-
|
|
94
|
-
validatedForm: 之前完成校验的所有数据的字典。
|
|
95
|
-
|
|
96
|
-
返回:
|
|
97
|
-
|
|
98
|
-
若有返回值,则为该参数的最终值。
|
|
99
|
-
'''
|
|
100
|
-
|
|
101
|
-
self._scope: Literal['form', 'headers', 'args', 'path', 'cookie'] = scope
|
|
102
|
-
self.key: str = key
|
|
103
|
-
self._type: List[object | Callable] | object | Callable = type
|
|
104
|
-
self._expected_type: str | None = type.__name__ if expected_type is None and self._type is not None else expected_type
|
|
105
|
-
self.required: bool = required
|
|
106
|
-
self._default: Any = default
|
|
107
|
-
self._pattern: str | None = pattern
|
|
108
|
-
self.min: object | None = min
|
|
109
|
-
self.max: object | None = max
|
|
110
|
-
if self.min is not None and self.max is not None and self.min > self.max:
|
|
111
|
-
raise ValueError('最小范围不能大于最大范围')
|
|
112
|
-
self._enum: List[Any] = enum
|
|
113
|
-
self._fn: Callable | None = fn
|
|
114
|
-
self._response: Response | None = response
|
|
115
|
-
|
|
116
|
-
async def _validate(self, app: 'App', *args, request: Request, validatedForm: Dict[str, object], **kwargs):
|
|
117
|
-
if f'{self.scope}.{self.key}' not in validatedForm:
|
|
118
|
-
if self.scope == 'path':
|
|
119
|
-
validatedForm[f'{self.scope}.{self.key}'] = kwargs[self.key]
|
|
120
|
-
else:
|
|
121
|
-
validatedForm[f'{self.scope}.{self.key}'] = (getattr(request, self.scope) or {}).get(self.key)
|
|
122
|
-
|
|
123
|
-
if validatedForm[f'{self.scope}.{self.key}'] is None:
|
|
124
|
-
if self.required:
|
|
125
|
-
raise ValidateError(self.response or Response(app._text.validator_requiredMessage(self.scope, self.key), 400))
|
|
126
|
-
validatedForm[f'{self.scope}.{self.key}'] = self.default
|
|
127
|
-
|
|
128
|
-
if validatedForm[f'{self.scope}.{self.key}'] is not None:
|
|
129
|
-
if self.type is not None:
|
|
130
|
-
try:
|
|
131
|
-
if isinstance(self.type, list):
|
|
132
|
-
for type in self.type:
|
|
133
|
-
validatedForm[f'{self.scope}.{self.key}'] = type(validatedForm[f'{self.scope}.{self.key}'])
|
|
134
|
-
else:
|
|
135
|
-
validatedForm[f'{self.scope}.{self.key}'] = self.type(validatedForm[f'{self.scope}.{self.key}'])
|
|
136
|
-
except:
|
|
137
|
-
raise ValidateError(self.response or Response(app._text.validator_typeMessage(self.scope, self.key, self.expected_type), 400))
|
|
138
|
-
|
|
139
|
-
if self.pattern and not re.match(self.pattern, validatedForm[f'{self.scope}.{self.key}']):
|
|
140
|
-
raise ValidateError(self.response or Response(app._text.validator_patternMessage(self.scope, self.key), 400))
|
|
141
|
-
|
|
142
|
-
if self.min and validatedForm[f'{self.scope}.{self.key}'] < self.min:
|
|
143
|
-
raise ValidateError(self.response or Response(app._text.validator_minMessage(self.scope, self.key, self.min), 400))
|
|
144
|
-
|
|
145
|
-
if self.max and validatedForm[f'{self.scope}.{self.key}'] > self.max:
|
|
146
|
-
raise ValidateError(self.response or Response(app._text.validator_maxMessage(self.scope, self.key, self.max), 400))
|
|
147
|
-
|
|
148
|
-
if self.enum and validatedForm[f'{self.scope}.{self.key}'] not in self.enum:
|
|
149
|
-
raise ValidateError(self.response or Response(app._text.validator_enumMessage(self.scope, self.key, self.enum), 400))
|
|
150
|
-
|
|
151
|
-
if self.fn:
|
|
152
|
-
try:
|
|
153
|
-
_value = await self.fn(*args, **{
|
|
154
|
-
'request': request,
|
|
155
|
-
'validatedForm': validatedForm,
|
|
156
|
-
**kwargs
|
|
157
|
-
})
|
|
158
|
-
if _value is not None:
|
|
159
|
-
validatedForm[f'{self.scope}.{self.key}'] = _value
|
|
160
|
-
except ValidateError as e:
|
|
161
|
-
raise ValidateError(e.response or self.response or Response(status = 500))
|
|
162
|
-
|
|
163
|
-
@property
|
|
164
|
-
def scope(self) -> Literal['form', 'headers', 'args', 'path', 'cookie']:
|
|
165
|
-
'''
|
|
166
|
-
域范围。
|
|
167
|
-
|
|
168
|
-
- form: request.form。
|
|
169
|
-
|
|
170
|
-
- headers: request.headers。
|
|
171
|
-
|
|
172
|
-
- args: request.args。
|
|
173
|
-
|
|
174
|
-
- path: request.path中的动态路由。
|
|
175
|
-
|
|
176
|
-
- cookie: request.cookie
|
|
177
|
-
'''
|
|
178
|
-
|
|
179
|
-
return self._scope
|
|
180
|
-
|
|
181
|
-
@scope.setter
|
|
182
|
-
def scope(self, value: Literal['form', 'headers', 'args', 'path', 'cookie']):
|
|
183
|
-
self._scope = value
|
|
184
|
-
|
|
185
|
-
@property
|
|
186
|
-
def type(self) -> object | Callable | None:
|
|
187
|
-
'''
|
|
188
|
-
遵循`type(value) -> object`的类或函数;支持list按顺序转换类型。
|
|
189
|
-
'''
|
|
190
|
-
|
|
191
|
-
return self._type
|
|
192
|
-
|
|
193
|
-
@type.setter
|
|
194
|
-
def type(self, value: object | Callable | None):
|
|
195
|
-
self._type = value
|
|
196
|
-
|
|
197
|
-
@property
|
|
198
|
-
def expected_type(self) -> str:
|
|
199
|
-
'''
|
|
200
|
-
期望的数据类型,不参与校验,仅供提示。
|
|
201
|
-
'''
|
|
202
|
-
|
|
203
|
-
return self._expected_type
|
|
204
|
-
|
|
205
|
-
@expected_type.setter
|
|
206
|
-
def expected_type(self, value: str | None):
|
|
207
|
-
self._expected_type = self.type.__name__ if value is None and self.type is not None else value
|
|
208
|
-
|
|
209
|
-
@property
|
|
210
|
-
def default(self) -> Any:
|
|
211
|
-
'''
|
|
212
|
-
默认值;该值仍会执行校验流程,而不是直接应用。
|
|
213
|
-
'''
|
|
214
|
-
|
|
215
|
-
return self._default
|
|
216
|
-
|
|
217
|
-
@default.setter
|
|
218
|
-
def default(self, value: Any):
|
|
219
|
-
self._default = value
|
|
220
|
-
|
|
221
|
-
@property
|
|
222
|
-
def pattern(self) -> str | None:
|
|
223
|
-
'''
|
|
224
|
-
对于`str`类型的数据,使用正则表达式进行验证。
|
|
225
|
-
'''
|
|
226
|
-
|
|
227
|
-
return self._pattern
|
|
228
|
-
|
|
229
|
-
@pattern.setter
|
|
230
|
-
def pattern(self, value: str | None):
|
|
231
|
-
self._pattern = value
|
|
232
|
-
|
|
233
|
-
@property
|
|
234
|
-
def enum(self) -> List[Any]:
|
|
235
|
-
'''
|
|
236
|
-
指定某些特定的值。
|
|
237
|
-
'''
|
|
238
|
-
|
|
239
|
-
return self._enum
|
|
240
|
-
|
|
241
|
-
@enum.setter
|
|
242
|
-
def enum(self, value: List[Any]):
|
|
243
|
-
self._enum = value
|
|
244
|
-
|
|
245
|
-
@property
|
|
246
|
-
def fn(self) -> Callable | None:
|
|
247
|
-
'''
|
|
248
|
-
自定义校验函数;该函数将在所有预设校验完成后执行。
|
|
249
|
-
|
|
250
|
-
该函数结构应为:
|
|
251
|
-
|
|
252
|
-
```python
|
|
253
|
-
from typing import Dict, Any
|
|
254
|
-
|
|
255
|
-
async def fn(*args, validatedForm: Dict[str, Any], **kwargs) -> Any:
|
|
256
|
-
...
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
参数:
|
|
260
|
-
|
|
261
|
-
validatedForm: 之前完成校验的所有数据的字典。
|
|
262
|
-
|
|
263
|
-
返回:
|
|
264
|
-
|
|
265
|
-
若有返回值,则为该参数的最终值。
|
|
266
|
-
'''
|
|
267
|
-
|
|
268
|
-
return self._fn
|
|
269
|
-
|
|
270
|
-
@fn.setter
|
|
271
|
-
def fn(self, value: Callable | None):
|
|
272
|
-
self._fn = value
|
|
273
|
-
|
|
274
|
-
@property
|
|
275
|
-
def response(self) -> Response | None:
|
|
276
|
-
'''
|
|
277
|
-
校验失败返回的响应体;若为`None`,则使用系统默认值。
|
|
278
|
-
'''
|
|
279
|
-
|
|
280
|
-
return self._response
|
|
281
|
-
|
|
282
|
-
@response.setter
|
|
283
|
-
def response(self, value: Response | None):
|
|
284
|
-
self._response = value
|
|
285
|
-
|
|
286
|
-
def validator(validators: List[Validator]):
|
|
287
|
-
'''
|
|
288
|
-
为路由函数添加校验装饰器。
|
|
289
|
-
|
|
290
|
-
校验通过后,路由函数会收到一个`validatedForm: Dict[str, Any]`参数,该参数为转换后的数据字典,key为`f'{validator.scope}.{validator.key}'`;在校验过程中,该参数也会在校验器中传递,其中包含了已经通过校验的数据。
|
|
291
|
-
|
|
292
|
-
下面是一个综合示例:
|
|
293
|
-
|
|
294
|
-
```python
|
|
295
|
-
import datetime
|
|
296
|
-
from typing import Any
|
|
297
|
-
|
|
298
|
-
from CheeseAPI import app, validator, Validator, Mail
|
|
299
|
-
|
|
300
|
-
async def birthDateValidator(*args, validatedForm, **kwargs):
|
|
301
|
-
date = datetime.datetime.now()
|
|
302
|
-
if validatedForm['form.birthDate'] > date:
|
|
303
|
-
raise ValidateError(Response('出生日期异常', 400))
|
|
304
|
-
|
|
305
|
-
@app.route.get('/')
|
|
306
|
-
@validator([
|
|
307
|
-
Validator('form', 'name', required = True),
|
|
308
|
-
Validator('form', 'birthDate', type = [ float, datetime.datetime.fromtimestamp ], expected_type = 'timestamp', fn = birthDateValidator),
|
|
309
|
-
Validator('form', 'gender', enum = [ 'man', 'woman', None ]),
|
|
310
|
-
Validator('form', 'idCard', pattern = r'^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[1-2]\\d|3[0-1])\\d{3}[\\dXx]$', response = Response('身份证格式错误', 400))
|
|
311
|
-
])
|
|
312
|
-
async def test(*args, validatedForm: Dict[str, Any], **kwargs):
|
|
313
|
-
...
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
将一个校验器拆分为两个,可以分布执行校验,以实现优先统一校验某一部分;下面示例将展示如何将birthDate的自定义校验放到所有校验器都完成之后执行:
|
|
317
|
-
|
|
318
|
-
```python
|
|
319
|
-
import datetime
|
|
320
|
-
from typing import Any
|
|
321
|
-
|
|
322
|
-
from CheeseAPI import app, validator, Validator, Mail
|
|
323
|
-
|
|
324
|
-
async def birthDateValidator(*args, validatedForm, **kwargs):
|
|
325
|
-
date = datetime.datetime.now()
|
|
326
|
-
if validatedForm['form.birthDate'] > date:
|
|
327
|
-
raise ValidateError(Response('出生日期异常', 400))
|
|
328
|
-
|
|
329
|
-
@app.route.get('/')
|
|
330
|
-
@validator([
|
|
331
|
-
Validator('form', 'name', required = True),
|
|
332
|
-
Validator('form', 'birthDate', type = [ float, datetime.datetime.fromtimestamp ], expected_type = 'timestamp'),
|
|
333
|
-
Validator('form', 'gender', enum = [ 'man', 'woman', None ]),
|
|
334
|
-
Validator('form', 'idCard', pattern = r'^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[1-2]\\d|3[0-1])\\d{3}[\\dXx]$', response = Response('身份证格式错误', 400)),
|
|
335
|
-
Validator('form', 'birthDate', fn = birthDateValidator)
|
|
336
|
-
])
|
|
337
|
-
async def test(*args, validatedForm: Dict[str, Any], **kwargs):
|
|
338
|
-
...
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
由于在执行自定义校验前已经完成了类型校验,就不再需要重复填写,可直接获取`validatedForm['form.birthDate']`。
|
|
342
|
-
|
|
343
|
-
- Args
|
|
344
|
-
|
|
345
|
-
- validators: 校验器会按顺序执行。
|
|
346
|
-
'''
|
|
347
|
-
|
|
348
|
-
def wrapper(fn):
|
|
349
|
-
@wraps(fn)
|
|
350
|
-
async def decorator(*args, **kwargs):
|
|
351
|
-
from CheeseAPI.app import app
|
|
352
|
-
|
|
353
|
-
validatedForm: Dict[str, object] = {}
|
|
354
|
-
for validator in validators:
|
|
355
|
-
try:
|
|
356
|
-
await validator._validate(app, *args, **{
|
|
357
|
-
**kwargs,
|
|
358
|
-
'validatedForm': validatedForm
|
|
359
|
-
})
|
|
360
|
-
except ValidateError as e:
|
|
361
|
-
return e.response
|
|
362
|
-
|
|
363
|
-
return await fn(*args, **{
|
|
364
|
-
**kwargs,
|
|
365
|
-
'validatedForm': validatedForm
|
|
366
|
-
})
|
|
367
|
-
return decorator
|
|
368
|
-
return wrapper
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|