皮皮网
皮皮网

【阿拉德之怒深渊源码】【全局异常源码】【源码燕窝真假】validator源码

来源:tinysoftware 源码 发表时间:2024-11-23 07:43:39

1.说说 Python 的元编程
2.SpringBoot Validation参数校验 详解自定义注解规则和分组校验
3.详解.NET Model ValidationAttribute的使用
4.async-validator源码解析(四):Schema类
5.async-validator源码解析(二):rule

validator源码

说说 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还提供了额外的阿拉德之怒深渊源码约束。校验消息支持表达式,允许自定义错误描述,通过ValidationMessages_zh_CN.properties文件配置。

       对于复杂的场景,分组校验必不可少。例如,全局异常源码允许新用户注册时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分支,探索每个文件的详细代码解析。

相关栏目:探索