26
2019.2
自动验证
作者: POPASP
类型检查只是针对数据库级别的验证,所以系统还内置了数据对象的自动验证功能来完成模型的业务规则验证,而大多数情况下面,数据对象是由表单提交的POST数据创建。需要使用系统的自动验证功能,只需要在Model类的init方法中定义 `db.validate_` 属性,它是由多个验证因子组成的二维数组。
验证因子格式:
`array(验证字段,验证规则,错误提示,[验证条件,附加规则,验证时间])`
| 项目 | 可选性 | 说明 |
| ------------- | ------------- |------------- |
| 验证字段 | 必须 | 需要验证的表单字段名称,这个字段不一定是数据库字段,也可以是表单的一些辅助字段,例如确认密码和验证码等等。有个别验证规则和字段无关的情况下,验证字段是可以随意设置的,例如expire有效期规则是和表单字段无关的。 |
| 验证规则 | 必须 | 要进行验证的规则,需要结合附加规则,如果在使用正则验证的附加规则情况下,系统还内置了一些常用正则验证的规则,可以直接作为验证规则使用,包括:require 字段必须、email 邮箱、url URL地址、currency 货币、number 数字等等 |
| 提示信息 | 必须 | 用于验证失败后的提示信息定义 |
| 验证条件 | 可选 | 包含下面几种情况: 0(EXISTS_VALIDATE) 存在字段就验证(默认) ; 1(MUST_VALIDATE) 必须验证; 2(VALUE_VALIDATE) 值不为空的时候验证 |
|附加规则 | 可选 | 配合验证规则使用,包括下面一些规则: regex 正则验证,定义的验证规则是一个正则表达式(默认) ; function 函数验证,定义的验证规则是一个函数名;callback 方法验证,定义的验证规则是当前模型类的一个方法;confirm 验证表单中的两个字段是否相同,定义的验证规则是一个字段名;equal 验证是否等于某个值,该值由前面的验证规则定义;in验证是否在某个范围内,定义的验证规则必须是一个数组;length验证长度,定义的验证规则可以是一个数字(表示固定长度)或者数字范围(例如3,12 表示长度从3到12的范围);between验证范围,定义的验证规则表示范围,可以使用字符串或者数组,例如1,31或者array(1,31);expire验证是否在有效期,定义的验证规则表示时间范围,可以到时间,例如可以使用 2012-1-15,2013-1-15 表示当前提交有效期在2012-1-15到2013-1-15之间,也可以使用时间戳定义;ip_allow 验证IP是否允许,定义的验证规则表示允许的IP地址列表,用逗号分隔,例如 202.1.2.5,202.1.2.6;ip_deny 验证IP是否禁止,定义的验证规则表示禁止的ip地址列表,用逗号分隔,例如201.12.2.5,201.12.2.6;unique 验证是否唯一,系统会根据字段目前的值查询数据库来判断是否存在相同的值。|
| 验证时间 | 可选 | 1(MODEL_INSERT) 新增数据时候验证 ; 2( MODEL_UPDATE) 编辑数据时候验证;3(MODEL_BOTH) 全部情况下验证(默认) |
```brush:vb
<%
Class Post
Public Sub init
db.tableName = "post"
db.prikey = "post_id"
db.validate_ = array( _
array( "title","2,50","标题长度应为2~50!",2,"length" ), _
array( "title","","unique 数据表中已经存在",2,"unique" ), _
array( "title","","exists 数据表中不存在该值",2,"exists" ), _
array( "add_time","","添加时间不是日期格式!",2,"date" ), _
array( "password","6,20","密码长度必须为6至20位",1,"length" ), _
array( "password","\w","密码必须由数字与英文字母组成",1 ), _
array( "re_password","password","两次输入的密码不相同",2,"equal" ), _
array( "in","男,女,保密","in 值的范围不正确!",0,"in" ), _
array( "notin","1,3,5,7,11","notin 值的范围不正确!",1,"notin" ), _
array( "between","1,10","between 值的范围不正确!",2,"between" ), _
array( "notbetween","1,10","notbetween 值的范围不正确!",2,"notbetween" ), _
array( "eq","张三","eq 值不正确!",2,"eq" ), _
array( "neq","李四","neq 值不正确!",2,"neq" ), _
array( "length","7","length 长度不正确!",2,"length" ), _
array( "expire","2015-3-1,2015-5-1","expire 过期了!",2,"expire" ), _
array( "ip_allow","127.0.0.1,127.0.0.2","ip_allow 不允许的IP",2,"ip_allow" ), _
array( "regex","\w","regex 正则不匹配",2 ), _
array( "chinese","chinese","chinese 必须是中文",2 ), _
array( "function","testFunc","function 没通过验证",2,"function" ), _
array( "callback","PostModel.testFunc","callback 没通过验证",2,"callback" ) _
)
End Sub
Public function testFunc( val )
testFunc = ( val >10 )
End Function
End Class
%>
```
当使用系统的create方法创建数据对象的时候会自动进行数据验证操作,代码示例:
```brush:vb
'运行在Action中
Sub validate
dim dict
set dict = D_()
dict("title")="测试2"
dict.add "add_time",now()
dict.add "password","11111"
dict.add "re_password","11111"
dict.add "in" , "男"
dict.add "notin","5"
dict.add "between","11"
dict.add "notbetween","1,10"
dict.add "eq","张三"
dict.add "neq","李四"
dict.add "length","7"
dict.add "expire","2015-6-1"
dict.add "ip_allow","127.0.0.1"
dict.add "regex","||||"
dict.add "chinese","abc"
dict.add "callback",1
dict.add "function",1
M_("post").db.patchValidate = true '验证所有项,false时一旦遇到未通过验证则中止验证
var_export M_("post").db.data(dict).Create( 1 )
var_export M_("post").db.error
End Sub
```
结果如下:
> False
> ["exists 数据表中不存在该值", "密码长度必须为6至20位", "两次输入的密码不相同", "notin 值的范围不正确!", "between 值的范围不正确!", "neq 值不正确!", "length 长度不正确!", "expire 过期了!", "regex 正则不匹配", "chinese 必须是中文", "callback 没通过验证"]
为了演示的需要,这里假设了很多字段,当然这些字段并不存在于数据表中,从“in”开始到“callback”这些字段都是假设的。
`db.patchValidate`的默认值为False,默认一旦遇到未通过验证的某条规则时就会中止验证,此时的`m.db.error`为第一条错误提示。为true时,则会以所有项进行验证,`m.db.error`为数组,里面包含所有错误提示。
通常来说,每个数据表对应的验证规则是相对固定的,但是有些特殊的情况下面可能会改变验证规则,我们可以动态的改变验证规则来满足不同条件下面的验证:
```brush:vb
m.db.validate_ = array( _
array( "title","2,50","标题长度应为2~50!",2,"length" ), _
array( "title","","unique 数据表中已经存在",2,"unique" ), _
array( "title","","exists 数据表中不存在该值",2,"exists" ), _
array( "add_time","","添加时间不是日期格式!",2,"date" ), _
array( "password","6,20","密码长度必须为6至20位",1,"length" ), _
array( "password","\w","密码必须由数字与英文字母组成",1 ), _
array( "re_password","password","两次输入的密码不相同",2,"equal" ) _
)
result = m.db.data(dict).create(1)
if not result then
' 如果创建失败 表示验证没有通过 输出错误提示信息
call that.error( array(m.db.error , "提示数据未通过验证",0) )
else
'验证通过 可以进行其他数据操作
end if
```
### 批量验证
支持数据的批量验证功能,只需要在模型类里面设置patchValidate属性为true( 默认为false),设置批处理验证后,m.db.error 返回的错误信息是一个数组。
### 手动验证
在使用Create方法时会自动验证数据。如果想手动验证,也是可以的。
使用**db.AutoValidate**方法
比如:
```brush:vb
M_("post").db.patchValidate = true '验证所有项,false时一旦遇到未通过验证则中止验证
var_export M_("post").db.AutoValidate(dict,1) '跟Create的验证效果是一样的。
var_export M_("post").db.error
```
使用**POPASP_AUTOVALIDATE**类中的方法**handle**
handle参数说明
| 参数 | 说明 |
| ------------- | ------------- |
| validate | 符合验证规则的数组 |
| data | 验证数据,Dictionary对象 |
| mode | 验证时间。1(MODEL_INSERT) 新增数据时候验证 ; 2( MODEL_UPDATE) 编辑数据时候验证;3(MODEL_BOTH) 全部情况下验证(默认) |
此时有两点要特别注意,因为unique与exists验证规则涉及到调用数据库,必须要提供表名,如下:
```brush:vb
db.validate_ = array(
array( "title","post","unique 数据表中已经存在",2,"unique" ), _
array( "title","post","exists 数据表中不存在该值",2,"exists" ), _
)
```
```brush:vb
' 运行在Action中
sub autoValidate_handle
dim dict, validate_ , obj
set dict = D_()
validate_ = array( _
array( "title","2,50","标题长度应为2~50!",2,"length" ), _
array( "title","post","unique 数据表中已经存在",2,"unique" ), _
array( "title","post","exists 数据表中不存在该值",2,"exists" ), _
array( "add_time","","添加时间不是日期格式!",2,"date" ), _
array( "password","6,20","密码长度必须为6至20位",1,"length" ), _
array( "password","\w","密码必须由数字与英文字母组成",1 ), _
array( "re_password","password","两次输入的密码不相同",2,"equal" ), _
array( "in","男,女,保密","in 值的范围不正确!",0,"in" ), _
array( "notin","1,3,5,7,11","notin 值的范围不正确!",1,"notin" ), _
array( "between","1,10","between 值的范围不正确!",2,"between" ), _
array( "notbetween","1,10","notbetween 值的范围不正确!",2,"notbetween" ), _
array( "eq","张三","eq 值不正确!",2,"eq" ), _
array( "neq","李四","neq 值不正确!",2,"neq" ), _
array( "length","7","length 长度不正确!",2,"length" ), _
array( "expire","2015-3-1,2015-5-1","expire 过期了!",2,"expire" ), _
array( "ip_allow","127.0.0.1,127.0.0.2","ip_allow 不允许的IP",2,"ip_allow" ), _
array( "regex","\w","regex 正则不匹配",2 ), _
array( "chinese","chinese","chinese 必须是中文",2 ), _
array( "function","testFunc","function 没通过验证",2,"function" ), _
array( "callback","PostModel.testFunc","callback 没通过验证",2,"callback" ) _
)
dict("title")="测试2"
dict.add "add_time",now()
dict.add "password","11111"
dict.add "re_password","11111"
dict.add "in" , "男"
dict.add "notin","5"
dict.add "between","11"
dict.add "notbetween","1,10"
dict.add "eq","张三"
dict.add "neq","李四"
dict.add "length","7"
dict.add "expire","2015-6-1"
dict.add "ip_allow","127.0.0.1"
dict.add "regex","||||"
dict.add "chinese","abc"
dict.add "callback",1
dict.add "function",1
set obj = P_("AUTOVALIDATE")
obj.patchValidate = true
var_export obj.handle( validate_ , dict, 1 )
var_export obj.error
end Sub
```
验证结果如下:
> False
> ["exists 数据表中不存在该值", "密码长度必须为6至20位", "两次输入的密码不相同", "notin 值的范围不正确!", "between 值的范围不正确!", "neq 值不正确!", "length 长度不正确!", "expire 过期了!", "regex 正则不匹配", "chinese 必须是中文", "callback 没通过验证"]