1.说说 Python çå
ç¼ç¨
2.SpringBoot Validation参数校验 详解自定义注解规则和分组校验
3.详解.NET Model ValidationAttributeç使ç¨
4.async-validator源码解析(四):Schema类
5.async-validator源码解析(二):rule
说说 Python çå ç¼ç¨
æå°å è¿ä¸ªåï¼ä½ ä¹è®¸ä¼æ³å°å æ°æ®ï¼å æ°æ®å°±æ¯æè¿°æ°æ®æ¬èº«çæ°æ®ï¼å 类就æ¯ç±»çç±»ï¼ç¸åºçå ç¼ç¨å°±æ¯æ述代ç æ¬èº«ç代ç ï¼å ç¼ç¨å°±æ¯å ³äºå建æä½æºä»£ç (æ¯å¦ä¿®æ¹ãçææå è£ åæ¥ç代ç )çå½æ°åç±»ã主è¦ææ¯æ¯ä½¿ç¨è£ 饰å¨ãå ç±»ãæ述符类ãæ¬æç主è¦ç®çæ¯å大家ä»ç»è¿äºå ç¼ç¨ææ¯ï¼å¹¶ä¸ç»åºå®ä¾æ¥æ¼ç¤ºå®ä»¬æ¯ææ ·å®å¶åæºä»£ç çè¡ä¸ºã
è£ é¥°å¨ è£ é¥°å¨å°±æ¯å½æ°çå½æ°ï¼å®æ¥åä¸ä¸ªå½æ°ä½ä¸ºåæ°å¹¶è¿åä¸ä¸ªæ°çå½æ°ï¼å¨ä¸æ¹ååæ¥å½æ°ä»£ç çæ åµä¸ä¸ºå ¶å¢å æ°çåè½ï¼æ¯å¦æ常ç¨ç计æ¶è£ 饰å¨ï¼
from functools import wrapsdef timeit(logger=None):"""èæ¶ç»è®¡è£ 饰å¨ï¼åä½æ¯ç§ï¼ä¿ç 4 ä½å°æ°"""def decorator(func):@wraps(func)def wrapper(*args, **kwargs):start = time.time()result = func(*args, **kwargs)end = time.time()if logger:logger.info(f"{ func.__name__} cost { end - start :.4f} seconds")else:print(f"{ func.__name__} cost { end - start :.4f} seconds")return resultreturn wrapperreturn decorator(注ï¼æ¯å¦ä¸é¢ä½¿ç¨ @wraps(func) 注解æ¯å¾éè¦çï¼ å®è½ä¿çåå§å½æ°çå æ°æ®) åªéè¦å¨åæ¥çå½æ°ä¸é¢å ä¸ @timeit() å³å¯ä¸ºå ¶å¢å æ°çåè½ï¼
@timeit()def test_timeit():time.sleep(1)test_timeit()#test_timeit cost 1. secondsä¸é¢ç代ç è·ä¸é¢è¿æ ·åçæææ¯ä¸æ ·çï¼
test_timeit = timeit(test_timeit)test_timeit()è£ é¥°å¨çæ§è¡é¡ºåº å½æå¤ä¸ªè£ 饰å¨çæ¶åï¼ä»ä»¬çè°ç¨é¡ºåºæ¯æä¹æ ·çï¼
åå¦æè¿æ ·ç代ç ï¼è¯·é®æ¯å æå° Decorator1 è¿æ¯ Decorator2 ?
from functools import wrapsdef decorator1(func):@wraps(func)def wrapper(*args, **kwargs):print('Decorator 1')return func(*args, **kwargs)return wrapperdef decorator2(func):@wraps(func)def wrapper(*args, **kwargs):print('Decorator 2')return func(*args, **kwargs)return wrapper@decorator1@decorator2def add(x, y):return x + yadd(1,2)# Decorator 1# Decorator 2åçè¿ä¸ªé®é¢ä¹åï¼æå ç»ä½ æ个形象çæ¯å»ï¼è£ 饰å¨å°±åå½æ°å¨ç©¿è¡£æï¼ç¦»å®æè¿çæå ç©¿ï¼ç¦»å¾è¿çæåç©¿ï¼ä¸ä¾ä¸ decorator1 æ¯å¤å¥ï¼decorator2 æ¯å è¡£ã
add = decorator1(decorator2(add))
å¨è°ç¨å½æ°çæ¶åï¼å°±åè±è¡£æï¼å 解é¤æå¤é¢ç decorator1ï¼ä¹å°±æ¯å æå° Decorator1ï¼æ§è¡å° return func(
args, kwargs) çæ¶åä¼å»è§£é¤ decorator2ï¼ç¶åæå° Decorator2ï¼å次æ§è¡å° return func(
args, kwargs) æ¶ä¼çæ£æ§è¡ add() å½æ°ã
éè¦æ³¨æçæ¯æå°çä½ç½®ï¼å¦ææå°å符串ç代ç ä½äºè°ç¨å½æ°ä¹åï¼åä¸é¢è¿æ ·ï¼é£è¾åºçç»ææ£å¥½ç¸åï¼
def decorator1(func):@wraps(func)def wrapper(*args, **kwargs):result = func(*args, **kwargs)print('Decorator 1')return resultreturn wrapperdef decorator2(func):@wraps(func)def wrapper(*args, **kwargs):result = func(*args, **kwargs)print('Decorator 2')return resultreturn wrapperè£ é¥°å¨ä¸ä» å¯ä»¥å®ä¹ä¸ºå½æ°ï¼ä¹å¯ä»¥å®ä¹ä¸ºç±»ï¼åªè¦ä½ ç¡®ä¿å®å®ç°äº__call__() å __get__() æ¹æ³ã
å ç±» Python ä¸ææç±»ï¼objectï¼çå ç±»ï¼å°±æ¯ type ç±»ï¼ä¹å°±æ¯è¯´ Python ç±»çå建è¡ä¸ºç±é»è®¤ç type ç±»æ§å¶ï¼æ个æ¯å»ï¼type ç±»æ¯ææç±»çç¥å ãæ们å¯ä»¥éè¿ç¼ç¨çæ¹å¼æ¥å®ç°èªå®ä¹çä¸äºå¯¹è±¡å建è¡ä¸ºã
å®ä¸ä¸ªç±»ç»§æ¿ type ç±» Aï¼ç¶åè®©å ¶ä»ç±»çå ç±»æå Aï¼å°±å¯ä»¥æ§å¶ A çå建è¡ä¸ºãå ¸åçå°±æ¯ä½¿ç¨å ç±»å®ç°ä¸ä¸ªåä¾ï¼
class Singleton(type):def __init__(self, *args, **kwargs):self._instance = Nonesuper().__init__(*args, **kwargs)def __call__(self, *args, **kwargs):if self._instance is None:self._instance = super().__call__(*args, **kwargs)return self._instanceelse:return self._instanceclass Spam(metaclass=Singleton):def __init__(self):print("Spam!!!")å ç±» Singleton ç__init__å__new__ æ¹æ³ä¼å¨å®ä¹ Spam çæé´è¢«æ§è¡ï¼è __call__æ¹æ³ä¼å¨å®ä¾å Spam çæ¶åæ§è¡ã
descriptor ç±»ï¼æ述符类ï¼
descriptor å°±æ¯ä»»ä½ä¸ä¸ªå®ä¹äº __get__()ï¼__set__()æ __delete__()ç对象ï¼æè¿°å¨è®©å¯¹è±¡è½å¤èªå®ä¹å±æ§æ¥æ¾ãåå¨åå é¤çæä½ãè¿é举å®æ¹ææ¡£[1]ä¸ä¸ªèªå®ä¹éªè¯å¨çä¾åã
å®ä¹éªè¯å¨ç±»ï¼å®æ¯ä¸ä¸ªæ述符类ï¼åæ¶è¿æ¯ä¸ä¸ªæ½è±¡ç±»ï¼
from abc import ABC, abstractmethodclass Validator(ABC):def __set_name__(self, owner, name):self.private_name = '_' + namedef __get__(self, obj, objtype=None):return getattr(obj, self.private_name)def __set__(self, obj, value):self.validate(value)setattr(obj, self.private_name, value)@abstractmethoddef validate(self, value):passèªå®ä¹éªè¯å¨éè¦ä» Validator 继æ¿ï¼å¹¶ä¸å¿ é¡»æä¾ validate() æ¹æ³ä»¥æ ¹æ®éè¦æµè¯åç§çº¦æã
è¿æ¯ä¸ä¸ªå®ç¨çæ°æ®éªè¯å·¥å ·ï¼
OneOf éªè¯å¼æ¯ä¸ç»å约æçé项ä¹ä¸ã
class OneOf(Validator):def __init__(self, *options):self.options = set(options)def validate(self, value):if value not in self.options:raise ValueError(f'Expected { value!r} to be one of { self.options!r}')Number éªè¯å¼æ¯å¦ä¸º int æ floatãæ ¹æ®å¯éåæ°ï¼å®è¿å¯ä»¥éªè¯å¼å¨ç»å®çæå°å¼ææ大å¼ä¹é´ã
class Number(Validator):def __init__(self, minvalue=None, maxvalue=None):self.minvalue = minvalueself.maxvalue = maxvaluedef validate(self, value):if not isinstance(value, (int, float)):raise TypeError(f'Expected { value!r} to be an int or float')if self.minvalue is not None and value < self.minvalue:raise ValueError(f'Expected { value!r} to be at least { self.minvalue!r}')if self.maxvalue is not None and value > self.maxvalue:raise ValueError(f'Expected { value!r} to be no more than { self.maxvalue!r}')String éªè¯å¼æ¯å¦ä¸º strãæ ¹æ®å¯éåæ°ï¼å®å¯ä»¥éªè¯ç»å®çæå°ææ大é¿åº¦ãå®è¿å¯ä»¥éªè¯ç¨æ·å®ä¹ç predicateã
class String(Validator):def __init__(self, minsize=None, maxsize=None, predicate=None):self.minsize = minsizeself.maxsize = maxsizeself.predicate = predicatedef validate(self, value):if not isinstance(value, str):raise TypeError(f'Expected { value!r} to be an str')if self.minsize is not None and len(value) < self.minsize:raise ValueError(f'Expected { value!r} to be no smaller than { self.minsize!r}')if self.maxsize is not None and len(value) > self.maxsize:raise ValueError(f'Expected { value!r} to be no bigger than { self.maxsize!r}')if self.predicate is not None and not self.predicate(value):raise ValueError(f'Expected { self.predicate} to be true for { value!r}')å®é åºç¨æ¶è¿æ ·åï¼
@timeit()def test_timeit():time.sleep(1)test_timeit()#test_timeit cost 1. seconds0æè¿°å¨é»æ¢æ æå®ä¾çå建ï¼
@timeit()def test_timeit():time.sleep(1)test_timeit()#test_timeit cost 1. seconds1æåçè¯ å ³äº Python çå ç¼ç¨ï¼æ»ç»å¦ä¸ï¼
å¦æå¸ææäºå½æ°æ¥æç¸åçåè½ï¼å¸æä¸æ¹ååæçè°ç¨æ¹å¼ãä¸åéå¤ä»£ç ãæç»´æ¤ï¼å¯ä»¥ä½¿ç¨è£ 饰å¨æ¥å®ç°ã
å¦æå¸ææä¸äºç±»æ¥ææäºç¸åçç¹æ§ï¼æè å¨ç±»å®ä¹å®ç°å¯¹å ¶çæ§å¶ï¼æ们å¯ä»¥èªå®ä¹ä¸ä¸ªå ç±»ï¼ç¶å让å®ç±»çå ç±»æå该类ã
å¦æå¸æå®ä¾çå±æ§æ¥ææäºå ±åçç¹ç¹ï¼å°±å¯ä»¥èªå®ä¹ä¸ä¸ªæ述符类ã
以ä¸å°±æ¯æ¬æ¬¡å享çææå 容ï¼å¦æä½ è§å¾æç« è¿ä¸éï¼æ¬¢è¿å ³æ³¨å ¬ä¼å·ï¼Pythonç¼ç¨å¦ä¹ åï¼æ¯æ¥å¹²è´§å享ï¼å 容è¦çPythonçµå书ãæç¨ãæ°æ®åºç¼ç¨ãDjangoï¼ç¬è«ï¼äºè®¡ç®ççãææ¯åå¾ç¼ç¨å¦ä¹ ç½ï¼äºè§£æ´å¤ç¼ç¨ææ¯ç¥è¯ã
åæï¼/post/SpringBoot Validation参数校验 详解自定义注解规则和分组校验
SpringBoot中,Hibernate Validator作为Bean Validation的强大支持,通过注解进行便捷的字段验证。内置校验注解如@NotNull、@NotEmpty和@NotBlank提供了基本的非空检查,而Hibernate还提供了额外的eureka源码解析参数约束。校验消息支持表达式,允许自定义错误描述,通过ValidationMessages_zh_CN.properties文件配置。
对于复杂的场景,分组校验必不可少。例如,eos源码允许新用户注册时name字段为空,但在更新时需要非空。通过自定义分组接口(如Update)和@Validated注解,可以针对不同场景进行针对性校验。递归校验在处理嵌套对象的验证时非常有用,只需在相关属性上添加@Valid即可。
为了扩展框架以应对复杂业务,Validation允许用户自定义校验。创建自定义校验器时,需设置message、groups和payload,红包互换源码指定验证逻辑实现类。例如,ValidatorUtil工具类可以用于封装验证逻辑,如日期、枚举、手机号和金额验证。
在SpringBoot中,验证流程一般如下:用户请求接口,参数经过Validation API进行校验,通过则执行业务逻辑,否则抛出异常,小号源码由全局异常处理器处理。要深入了解这方面的内容,可以参考其他相关文章,如全局异常处理。
获取更多技术分享和源码,请关注我们的公众号猿人生,发送相关关键词获取资源。
详解.NET Model ValidationAttributeç使ç¨
ä¸ãç®ä»
ValidationAttribute éªè¯ç¹æ§ä¸è¬ç¨æ¥éªè¯æ°æ®çæ ¼å¼ï¼èå´ï¼æ¯å¦å¿ å¡«çï¼æ们éè¿å®çåç±»ç¹æ§ RangeãRequired çç¹æ§å¯ä»¥è½»æ¾å®ç°å¯¹æ°æ®çéªè¯ãä½æ¯å¯¹äºä¸äºç¹æ®éè¦çç¹æ§ï¼ç³»ç»èªå¸¦çç¹æ§å±éæ§å¾å¤§ï¼æ们ä¹å¯ä»¥èªå®ä¹æ©å±éè¦çç¹æ§ã ?
äºãASP.NET åºç¨ValidationAttribute åºç¨å¨ ASP.NET MVC ä¸æ¯å¸¸ç¨å¾ï¼ç¨äºå¯¹ HTPP Request åæ°å段å¾æ ¡éªï¼ä¸éè¿å¾ä¼è¿å æ示ç»è°ç¨æ¹ãæ¥ä¸éè¿ä¸ä¸ªç®åä¾åè¿è¡è¯´æä¸ä¸ï¼ä¾å使ç¨å¾æ¯ .NET Web API ã
2.1 Modelé¦å å®ä¹ä¸ä¸ª request model æ·»å ä¸äºå段ï¼å¦ Name æä¸ RequiredãMinLengthãMaxLength è¿æ ·çæ è®°è¿è¡éå¶
public?class?PersonalRequest{ public?int?Id?{ ?get;?set;?}[Required][MinLength(2),?MaxLength()]public?string?Name?{ ?get;?set;?}public?string?Sex?{ ?get;?set;?}[Required][MinLength(2),?MaxLength()]public?string?Address?{ ?get;?set;?}}2.2 Controllerå®ä¹å® request ?model åï¼å建ä¸ä¸ª ?API ï¼å°ä¸ä¸æ¥å®ä¹ç request model ä½ä¸ºåæ°ãæ¤æ¶å¨ request ?model å段ä¸æç ?RequiredãMinLengthãMaxLength æ è®°ï¼æ¯æ²¡æä»»ä½ä½ç¨çã
[HttpPost][Route("WebAPIApply")]public?IActionResult?WebAPIApply(PersonalRequest?request){ return?Ok();}2.4 Filter为äºè®© request ?model å段ä¸æç ?RequiredãMinLengthãMaxLength æ è®°çæï¼ MVC ä¸éè¦ä½¿ç¨å° Filter ã代ç å¦ä¸
public?class?ModelValidateFilter?:?IAsyncActionFilter{ public?async?Task?OnActionExecutionAsync(ActionExecutingContext?context,?ActionExecutionDelegate?next){ if?(!context.ModelState.IsValid){ var?allErrors?=?context.ModelState.Values.SelectMany(v?=>?v.Errors);var?message?=?string.Join("?|?",?allErrors.Select(e?=>?e.ErrorMessage));context.Result?=?new?JsonResult(new?{ ?results?=?allErrors?});}await?next();}}2.5 注åFilter å建å®æåï¼éè¦æ³¨åä¸ä¸ï¼è¿éæ们使ç¨å ¨å±æ¨¡å¼ãå¦ä¸
builder.Services.AddMvc(opt?=>{ opt.Filters.Add(new?ModelValidateFilter());});2.6 éªè¯ç¨åºå建å®æåï¼è°ç¨ API éªè¯ä¸ä¸ï¼å¦ä¸å¯¹ Name åå«èµå¼äº D Daã å¯ä»¥çå°å½æ¯ D çæ¶åï¼å段é¿åº¦ä¸º 1 ï¼filter 对 request ?model è¿è¡äºæ£ç¡®çæ ¡éª
ä¸ãç´æ¥éªè¯å®ä¾ç´æ¥éªè¯å®ä¾ï¼æ¯æå¯ä»¥å¯¹å®ä¹ model çå®ä¾ï¼èªå®ä¹ä»£ç 对é½è¿è¡æ ¡éªï¼è¿ç§ä½¿ç¨èµ·æ¥ç¸å¯¹çµæ´»ï¼ä½ä¹ä¸ç»å¸¸ä½¿ç¨ãæ¥ä¸éè¿ä¸ä¸ªç®åä¾åè¿è¡è¯´æä¸ä¸
3.1 Modelé¦å å®ä¹ä¸ä¸ª model æ·»å ä¸äºå段ï¼å¦ Name æä¸ RequiredãMinLengthãMaxLength è¿æ ·çæ è®°è¿è¡éå¶
public?class?Personal{ [Required]public?int?ID?{ ?get;?set;?}[Required][MinLength(2),?MaxLength(5)]public?string?Name?{ ?get;?set;?}}3.2 Validation Codeæ ¡éªä»£ç å¦ä¸ï¼å 对å®ä¹ç model è¿è¡äºå®ä¾åï¼ç¶åä½¿ç¨ Validator.TryValidateObject æ¹æ³è¿è¡æ ¡éªï¼å®é å·¥ä½ä¸éè¦èªå·±è¿è¡ä¸äºå°è£ ãå ³é®ä»£ç å¦ä¸ï¼
Personal?personal?=?new?Personal();//?personal.Name?=?"D";ValidationContext?validationContext?=?new?ValidationContext(personal);List<ValidationResult>?results?=?new?List<ValidationResult>();bool?isValid?=?Validator.TryValidateObject(personal,?validationContext,?results,?true);3.3 éªè¯ä»£ç åå®åï¼è°ç¨ API éªè¯ä¸ä¸ãé¦å ç´æ¥è°ç¨ï¼åºä¸ºä»£ç éé¢æ²¡æ对 Personal å®ä¾èµå¼ï¼æ£ç¡®çè¿è¡äº Required æ è®°çæ ¡éªå¯ä»¥ä¿®æ¹ä¸ä»£ç ?personal.Name = "Da"ï¼ç¶åå¯å¨ç¨åºï¼è°ç¨ä¸ä¸ API å¯ä»¥çå°æ ¡éªæ¯éè¿çå¯ä»¥ä¿®æ¹ä¸ä»£ç ?personal.Name = "D"ï¼ç¶åå¯å¨ç¨åºï¼è°ç¨ä¸ä¸ API å¯ä»¥çå°æ ¡éªæ¯ä¸éè¿çï¼å 为é¿åº¦ä¸º 1 没æ满足 MinLength(2)
åãèªå®ä¹ ValidationAttributeé¤äºå¯ä»¥ä½¿ç¨ Microsoft æä¾ç ValidationAttribute ï¼æ们è¿å¯ä»¥ä½¿ç¨èªå®ä¹ç ValidationAttribute ï¼å¾®è½¯è¿æ¹é¢æä¾äºå¯æ©å±æ§ãèªå®ä¹ç ValidationAttribute åªéç»§æ¿ ValidationAttributeï¼éå IsValid ãFormatErrorMessage å³å¯ã使ç¨æ¹é¢ä¸ Microsoft æä¾ç ValidationAttribute 使ç¨ä¸æ¨¡ä¸æ ·ãå¦ä¸ä»£ç
public?class?CanToIntAttribute?:?ValidationAttribute{ ///?<summary>///?IsValid?为?false?æ¶ï¼æ示å¾?error?ä¿¡æ¯///?</summary>///?<param?name="name"></param>///?<returns></returns>public?override?string?FormatErrorMessage(string?name){ return?$"{ name}?need?to?int";}///?<summary>///?éªè¯å½åå段å¾ç»æ///?</summary>///?<param?name="value"></param>///?<returns></returns>public?override?bool?IsValid(object?value){ int?num?=?0;return?int.TryParse(Convert.ToString(value),?out?num);}}public?class?PersonalRequest{ public?int?Id?{ ?get;?set;?}public?string?Name?{ ?get;?set;?}[CanToInt]public?string?Sex?{ ?get;?set;?}[Required][MinLength(2),?MaxLength()]public?string?Address?{ ?get;?set;?}}äºãæºç /post/
async-validator源码解析(四):Schema类
async-validator源码解析(四)深入核心:Schema类详解
在上篇中我们已经探讨了rule的细节,现在我们继续向上,关注async-validator库的基石——Schema类。尽管Schema类的本地生活源码代码较为复杂,但本文将从非核心的结构、属性和方法入手,同时在analysis分支的GitHub仓库中可以找到详细代码分析。
Schema类是async-validator库的典型使用方式,其构造和功能强大。首先,我们从构造函数开始解析,它分为三个步骤,其中定义方法(define)暂且跳过,因为其代码量大,会在后续章节单独讨论。
Schema类的构造函数涉及到messages.js中的defaultMessages,它提供了针对不同验证失败的模板提示。这允许用户在项目中根据需要定制错误提示。文档中提供了如何使用Schema.prototype.message方法自定义message的示例。
然而,message的深度合并存在一个局限,只能处理两层嵌套,但默认messages正好满足这一需求。关于警告控制,官方文档建议在实例化Schema前通过warning方法进行设置,以控制警告信息的显示。
此外,Schema类还提供了静态方法register,允许用户注册自定义的校验类型,尽管官方文档对此并未详述。这为开发者扩展库的功能提供了便利。
async-validator源码解析(二):rule
async-validator源码解析(二)深入探讨rule模块,解析其内部的校验逻辑和依赖工具函数。本文将逐步揭开rule目录的面纱,以及util.js中关键的format和isEmptyValue方法。
rule目录的核心是export的一系列校验方法,它们接受value、source、errors和options作为参数。value是当前字段的值,source是整个待校验的对象,而errors数组用于存储验证结果。options允许自定义验证消息。每种规则方法如required、whitespace、range等,都有特定的验证功能,例如检查必填性、空白字符、数值范围等。
format函数是个灵活的工具,根据传入参数的不同执行不同的格式化操作。而isEmptyValue则用于判断值是否为空,包括空字符串和空数组。
在rule目录中,type.js规则尤其有趣,通过组合简单的判断,区分了值的多种类型,如整数、浮点数、数组等。
后续文章将继续关注validator目录,完整揭示async-validator校验库的运作机制。点击github.com/MageeLin/asy.../analysis分支,探索每个文件的详细代码解析。