improve package gvalid

This commit is contained in:
John
2020-05-10 16:48:00 +08:00
parent 993d6e3076
commit aa5d3285eb
10 changed files with 221 additions and 235 deletions

View File

@ -223,7 +223,7 @@ func (m *Manager) init() {
files, _ := gfile.ScanDirFile(m.options.Path, "*.*", true)
if len(files) == 0 {
intlog.Printf(
"no i18n files found in configured directory:%s",
"no i18n files found in configured directory: %s",
m.options.Path,
)
return

View File

@ -13,57 +13,58 @@ import (
"github.com/gogf/gf/text/gregex"
)
/*
参考https://laravel.com/docs/5.5/validation#available-validation-rules
规则如下:
required 格式:required 说明:必需参数
required-if 格式:required-if:field,value,... 说明:必需参数(当任意所给定字段值与所给值相等时当field字段的值为value时当前验证字段为必须参数)
required-unless 格式:required-unless:field,value,... 说明:必需参数(当所给定字段值与所给值都不相等时当field字段的值不为value时当前验证字段为必须参数)
required-with 格式:required-with:field1,field2,... 说明:必需参数(当所给定任意字段值不为空时)
required-with-all 格式:required-with-all:field1,field2,... 说明:必须参数(当所给定所有字段值都不为空时)
required-without 格式:required-without:field1,field2,... 说明:必需参数(当所给定任意字段值为空时)
required-without-all 格式:required-without-all:field1,field2,...说明:必须参数(当所给定所有字段值都为空时)
date 格式:date 说明:参数为常用日期类型,格式:2006-01-02, 20060102, 2006.01.02
date-format 格式:date-format:format 说明判断日期是否为指定的日期格式format为Go日期格式(可以包含时间)
email 格式:email 说明EMAIL邮箱地址
phone 格式:phone 说明:手机号
telephone 格式:telephone 说明:国内座机电话号码,"XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
passport 格式:passport 说明:通用帐号规则(字母开头只能包含字母、数字和下划线长度在6~18之间)
password 格式:password 说明:通用密码(任意可见字符长度在6~18之间)
password2 格式:password2 说明:中等强度密码(在弱密码的基础上,必须包含大小写字母和数字)
password3 格式:password3 说明:强等强度密码(在弱密码的基础上,必须包含大小写字母、数字和特殊字符)
postcode 格式:postcode 说明:中国邮政编码
id-number 格式id-number 说明:公民身份证号码
luhn 格式luhn 说明:银行号验证
qq 格式:qq 说明腾讯QQ号码
ip 格式:ip 说明:IPv4/IPv6地址
ipv4 格式:ipv4 说明:IPv4地址
ipv6 格式:ipv6 说明:IPv6地址
mac 格式:mac 说明:MAC地址
url 格式:url 说明:URL
domain 格式:domain 说明:域名
length 格式:length:min,max 说明参数长度为min到max(长度参数为整形)注意中文一个汉字占3字节
min-length 格式:min-length:min 说明参数长度最小为min(长度参数为整形)注意中文一个汉字占3字节
max-length 格式:max-length:max 说明参数长度最大为max(长度参数为整形)注意中文一个汉字占3字节
between 格式:between:min,max 说明参数大小为min到max(支持整形和浮点类型参数)
min 格式:min:min 说明参数最小为min(支持整形和浮点类型参数)
max 格式:max:max 说明参数最大为max(支持整形和浮点类型参数)
json 格式:json 说明:判断数据格式为JSON
integer 格式:integer 说明:整数
float 格式:float 说明:浮点数(整数也是浮点数)
boolean 格式:boolean 说明:布尔值(1,true,on,yes:true | 0,false,off,no,"":false)
same 格式:same:field 说明参数值必需与field参数的值相同
different 格式:different:field 说明参数值不能与field参数的值相同
in 格式:in:value1,value2,... 说明:参数值应该在value1,value2,...中(字符串匹配)
not-in 格式:not-in:value1,value2,... 说明:参数值不应该在value1,value2,...中(字符串匹配)
regex 格式:regex:pattern 说明:参数值应当满足正则匹配规则pattern
*/
// Refer to Laravel validation: https://laravel.com/docs/5.5/validation#available-validation-rules
//
// All supported rules:
// required format: required brief: Required.
// required-if format: required-if:field,value,... brief: Required unless all given field and its value are equal.
// required-unless format: required-unless:field,value,... brief: Required unless all given field and its value are not equal.
// required-with format: required-with:field1,field2,... brief: Required if any of given fields are not empty.
// required-with-all format: required-with-all:field1,field2,... brief: Required if all of given fields are not empty.
// required-without format: required-without:field1,field2,... brief: Required if any of given fields are empty.
// required-without-all format: required-without-all:field1,field2,...brief: Required if all of given fields are empty.
// date format: date brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02
// date-format format: date-format:format brief: Custom date format.
// email format: email brief: Email address.
// phone format: phone brief: Phone number.
// telephone format: telephone brief: Telephone number, like: "XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
// passport format: passport brief: Universal passport format rule: Starting with letter, containing only numbers or underscores, length between 6 and 18
// password format: password brief: Universal password format rule1: Containing any visible chars, length between 6 and 18.
// password2 format: password2 brief: Universal password format rule2: Must meet password rule1, must contain lower and upper letters and numbers.
// password3 format: password3 brief: Universal password format rule3: Must meet password rule1, must contain lower and upper letters, numbers and special chars.
// postcode format: postcode brief: Postcode number.
// resident-id format: resident-id brief: Resident id number.
// bank-card format: bank-card brief: Bank card nunber.
// qq format: qq brief: Tencent QQ number.
// ip format: ip brief: IPv4/IPv6.
// ipv4 format: ipv4 brief: IPv4.
// ipv6 format: ipv6 brief: IPv6.
// mac format: mac brief: MAC.
// url format: url brief: URL.
// domain format: domain brief: Domain.
// length format: length:min,max brief: Length between :min and :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
// min-length format: min-length:min brief: Length is equal or greater than :min. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
// max-length format: max-length:max brief: Length is equal or lesser than :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
// between format: between:min,max brief: Range between :min and :max. It supports both integer and float.
// min format: min:min brief: Equal or greater than :min. It supports both integer and float.
// max format: max:max brief: Equal or lesser than :max. It supports both integer and float.
// json format: json brief: JSON.
// integer format: integer brief: Integer.
// float format: float brief: Float. Note that an integer is actually a float number.
// boolean format: boolean brief: Boolean(1,true,on,yes:true | 0,false,off,no,"":false)
// same format: same:field brief: Value should be the same as value of field.
// different format: different:field brief: Value should be different from value of field.
// in format: in:value1,value2,... brief: Value should be in: value1,value2,...
// not-in format: not-in:value1,value2,... brief: Value should not be in: value1,value2,...
// regex format: regex:pattern brief: Value should match custom regular expression pattern.
// 自定义错误信息: map[键名] => 字符串|map[规则]错误信息
// CustomMsg is the custom error message type,
// like: map[field] => string|map[rule]string
type CustomMsg = map[string]interface{}
// 解析单条sequence tag格式: [alias@]rule[...#msg...]
func parseSequenceTag(tag string) (name, rule, msg string) {
// parseSequenceTag parses one sequence tag to field, rule and error message.
// The sequence tag is like: [alias@]rule[...#msg...]
func parseSequenceTag(tag string) (field, rule, msg string) {
// Complete sequence tag.
// Eg: required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致
match, _ := gregex.MatchString(`\s*((\w+)\s*@){0,1}\s*([^#]+)\s*(#\s*(.*)){0,1}\s*`, tag)

View File

@ -102,40 +102,46 @@ var (
}
)
// 检测单条数据的规则:
// Check checks single value with specified rules.
//
// 1. value为需要校验的数据可以为任意基本数据类型
//
// 2. msgs为自定义错误信息由于同一条数据的校验规则可能存在多条为方便调用参数类型支持 string/map/struct/*struct,
// 允许传递多个自定义的错误信息如果类型为string那么中间使用"|"符号分隔多个自定义错误;
//
// 3. params参数为联合校验参数支持任意的map/struct/*struct类型对于需要联合校验的规则有效required-*、same、different
func Check(value interface{}, rules string, msgs interface{}, params ...interface{}) *Error {
// The parameter <value> can be any type of variable, which will be converted to string
// for validation.
// The parameter <rules> can be one or more rules, multiple rules joined using char '|'.
// The parameter <messages> specifies the custom error messages, which can be type of:
// string/map/struct/*struct.
// The optional parameter <params> specifies the extra validation parameters for some rules
// like: required-*、same、different, etc.
func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
if rules == "" {
return nil
}
// 内部会将参数全部转换为字符串类型进行校验
val := strings.TrimSpace(gconv.String(value))
data := make(map[string]string)
errorMsgs := make(map[string]string)
// It converts value to string and then does the validation.
var (
val = strings.TrimSpace(gconv.String(value))
data = make(map[string]string)
errorMsgs = make(map[string]string)
)
if len(params) > 0 {
for k, v := range gconv.Map(params[0]) {
data[k] = gconv.String(v)
}
}
// 自定义错误消息处理
msgArray := make([]string, 0)
customMsgMap := make(map[string]string)
switch v := msgs.(type) {
// Custom error messages handling.
var (
msgArray = make([]string, 0)
customMsgMap = make(map[string]string)
)
switch v := messages.(type) {
case string:
msgArray = strings.Split(v, "|")
default:
for k, v := range gconv.Map(msgs) {
for k, v := range gconv.Map(messages) {
customMsgMap[k] = gconv.String(v)
}
}
// Handle the char '|' in the rule,
// which makes this rule separated into multiple rules.
ruleItems := strings.Split(strings.TrimSpace(rules), "|")
// 规则项预处理, 主要解决规则中存在的"|"关键字符号
for i := 0; ; {
array := strings.Split(ruleItems[i], ":")
if _, ok := allSupportedRules[array[0]]; !ok {
@ -143,7 +149,10 @@ func Check(value interface{}, rules string, msgs interface{}, params ...interfac
ruleItems[i-1] += "|" + ruleItems[i]
ruleItems = append(ruleItems[:i], ruleItems[i+1:]...)
} else {
return newErrorStr("parse_error", "invalid rules:"+rules)
return newErrorStr(
"invalid_rules",
"invalid rules: "+rules,
)
}
} else {
i++
@ -176,6 +185,7 @@ func Check(value interface{}, rules string, msgs interface{}, params ...interfac
match = checkRequired(val, ruleKey, ruleVal, data)
// Length rules.
// It also supports length of unicode string.
case
"length",
"min-length",
@ -210,12 +220,12 @@ func Check(value interface{}, rules string, msgs interface{}, params ...interfac
// Date rules.
case "date":
// Standard date string, which must contain char '-'.
// Standard date string, which must contain char '-' or '.'.
if _, err := gtime.StrToTime(val); err == nil {
match = true
break
}
// Date that not contains char '-'.
// Date that not contains char '-' or '.'.
if _, err := gtime.StrToTime(val, "Ymd"); err == nil {
match = true
break
@ -329,26 +339,21 @@ func Check(value interface{}, rules string, msgs interface{}, params ...interfac
match = checkResidentId(val)
// Bank card number using LUHN algorithm.
case
"luhn",
"bank-card":
case "bank-card":
match = checkLuHn(val)
// Universal passport format rule:
// Starting with letter, containing only numbers or underscores, length between 6 and 18
// 通用帐号规则(字母开头只能包含字母、数字和下划线长度在6~18之间)
// Starting with letter, containing only numbers or underscores, length between 6 and 18.
case "passport":
match = gregex.IsMatchString(`^[a-zA-Z]{1}\w{5,17}$`, val)
// Universal password format rule1:
// Containing any visible chars, length between 6 and 18
// 通用密码(任意可见字符长度在6~18之间)
// Containing any visible chars, length between 6 and 18.
case "password":
match = gregex.IsMatchString(`^[\w\S]{6,18}$`, val)
// Universal password format rule2:
// Must meet password rule1, must contain lower and upper letters and numbers.
// 中等强度密码(在弱密码的基础上,必须包含大小写字母和数字)
case "password2":
if gregex.IsMatchString(`^[\w\S]{6,18}$`, val) && gregex.IsMatchString(`[a-z]+`, val) && gregex.IsMatchString(`[A-Z]+`, val) && gregex.IsMatchString(`\d+`, val) {
match = true
@ -356,7 +361,6 @@ func Check(value interface{}, rules string, msgs interface{}, params ...interfac
// Universal password format rule3:
// Must meet password rule1, must contain lower and upper letters, numbers and special chars.
// 强等强度密码(在弱密码的基础上,必须包含大小写字母、数字和特殊字符)
case "password3":
if gregex.IsMatchString(`^[\w\S]{6,18}$`, val) && gregex.IsMatchString(`[a-z]+`, val) && gregex.IsMatchString(`[A-Z]+`, val) && gregex.IsMatchString(`\d+`, val) && gregex.IsMatchString(`[^a-zA-Z0-9]+`, val) {
match = true
@ -416,7 +420,7 @@ func Check(value interface{}, rules string, msgs interface{}, params ...interfac
match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, val)
default:
errorMsgs[ruleKey] = "Invalid rule name:" + ruleKey
errorMsgs[ruleKey] = "Invalid rule name: " + ruleKey
}
// Error message handling.

View File

@ -15,23 +15,20 @@ import (
// 检测键值对参数Map
// rules参数支持 []string / map[string]string 类型,前面一种类型支持返回校验结果顺序(具体格式参考struct tag),后一种不支持;
// rules参数中得 map[string]string 是一个2维的关联数组第一维键名为参数键名第二维为带有错误的校验规则名称值为错误信息。
func CheckMap(params interface{}, rules interface{}, msgs ...CustomMsg) *Error {
// 将参数转换为 map[string]interface{}类型
func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
data := gconv.Map(params)
if data == nil {
return newErrorStr(
"invalid_params",
"invalid params type: convert to map[string]interface{} failed",
"invalid params type: convert to map failed",
)
}
// 真实校验规则数据结构
checkRules := make(map[string]string)
// 真实自定义错误信息数据结构
customMsgs := make(CustomMsg)
// 返回的顺序规则
errorRules := make([]string, 0)
// 返回的校验错误
errorMaps := make(ErrorMap)
var (
checkRules = make(map[string]string)
customMsgs = make(CustomMsg)
errorRules = make([]string, 0)
errorMaps = make(ErrorMap)
)
// 解析rules参数
switch v := rules.(type) {
// 支持校验错误顺序: []sequence tag
@ -69,13 +66,13 @@ func CheckMap(params interface{}, rules interface{}, msgs ...CustomMsg) *Error {
checkRules = v
}
// 自定义错误消息非必须参数优先级比rules参数中定义的错误消息更高
if len(msgs) > 0 && len(msgs[0]) > 0 {
if len(messages) > 0 && len(messages[0]) > 0 {
if len(customMsgs) > 0 {
for k, v := range msgs[0] {
for k, v := range messages[0] {
customMsgs[k] = v
}
} else {
customMsgs = msgs[0]
customMsgs = messages[0]
}
}
// 开始执行校验: 以校验规则作为基础进行遍历校验

View File

@ -6,56 +6,64 @@
package gvalid
import "strings"
import (
"github.com/gogf/gf/text/gregex"
"strings"
)
// 校验错误对象
// Error is the validation error for validation result.
type Error struct {
rules []string // 校验结果顺序(可能为nil),可保证返回校验错误的顺序性
errors ErrorMap // 完整的数据校验结果存储(map无序)
firstKey string // 第一条错误项键名(常用操作冗余数据),默认为空
firstItem map[string]string // 第一条错误项(常用操作冗余数据)默认为nil
rules []string // Rules by sequence, which is used for keeping error sequence.
errors ErrorMap // Error map.
firstKey string // The first error rule key(nil in default).
firstItem map[string]string // The first error rule value(nil in default).
}
// 校验错误信息: map[键名]map[规则名]错误信息
// ErrorMap is the validation error map:
// map[field]map[rule]message
type ErrorMap map[string]map[string]string
// 创建一个校验错误对象指针(校验错误)
// newError creates and returns a validation error.
func newError(rules []string, errors map[string]map[string]string) *Error {
for field, m := range errors {
for k, v := range m {
v = strings.Replace(v, ":attribute", field, -1)
m[k], _ = gregex.ReplaceString(`\s{2,}`, ` `, v)
}
errors[field] = m
}
return &Error{
rules: rules,
errors: errors,
}
}
// 创建一个校验错误对象指针(内部错误)
// newErrorStr creates and returns a validation error by string.
func newErrorStr(key, err string) *Error {
return &Error{
rules: nil,
errors: map[string]map[string]string{
"__gvalid__": {
key: err,
},
return newError(nil, map[string]map[string]string{
"__gvalid__": {
key: err,
},
}
})
}
// 获得规则与错误信息的map; 当校验结果为多条数据校验时返回第一条错误map(此时类似FirstItem)
// Map returns the first error message as map.
func (e *Error) Map() map[string]string {
_, m := e.FirstItem()
return m
}
// 获得原始校验结果ErrorMap
// Maps returns all error messages as map.
func (e *Error) Maps() ErrorMap {
return e.errors
}
// 只获取第一个键名的校验错误项
func (e *Error) FirstItem() (key string, msgs map[string]string) {
// FirstItem returns the field name and error messages for the first validation rule error.
func (e *Error) FirstItem() (key string, messages map[string]string) {
if e.firstItem != nil {
return e.firstKey, e.firstItem
}
// 有序
// By sequence.
if len(e.rules) > 0 {
for _, v := range e.rules {
name, _, _ := parseSequenceTag(v)
@ -66,7 +74,7 @@ func (e *Error) FirstItem() (key string, msgs map[string]string) {
}
}
}
// 无序
// No sequence.
for k, m := range e.errors {
e.firstKey = k
e.firstItem = m
@ -75,9 +83,9 @@ func (e *Error) FirstItem() (key string, msgs map[string]string) {
return "", nil
}
// 只获取第一个校验错误项的规则及错误信息
// FirstRule returns the first error rule and message string.
func (e *Error) FirstRule() (rule string, err string) {
// 有序
// By sequence.
if len(e.rules) > 0 {
for _, v := range e.rules {
name, rule, _ := parseSequenceTag(v)
@ -92,7 +100,7 @@ func (e *Error) FirstRule() (rule string, err string) {
}
}
}
// 无序
// No sequence.
for _, m := range e.errors {
for k, v := range m {
return k, v
@ -101,13 +109,14 @@ func (e *Error) FirstRule() (rule string, err string) {
return "", ""
}
// 只获取第一个校验错误项的错误信息
// FirstString returns the first error message as string.
// Note that the returned message might be different if it has no sequence.
func (e *Error) FirstString() (err string) {
_, err = e.FirstRule()
return
}
// 将所有错误信息构建称字符串,多个错误信息字符串使用"; "符号分隔
// String returns all error messages as string, multiple error messages joined using char ';'.
func (e *Error) String() string {
return strings.Join(e.Strings(), "; ")
}
@ -117,10 +126,10 @@ func (e *Error) Error() string {
return e.String()
}
// 只返回错误信息,构造成字符串数组返回
// Strings returns all error messages as string array.
func (e *Error) Strings() (errs []string) {
errs = make([]string, 0)
// 有序
// By sequence.
if len(e.rules) > 0 {
for _, v := range e.rules {
name, rule, _ := parseSequenceTag(v)
@ -136,7 +145,7 @@ func (e *Error) Strings() (errs []string) {
}
return errs
}
// 无序
// No sequence.
for _, m := range e.errors {
for _, err := range m {
errs = append(errs, err)

View File

@ -14,43 +14,43 @@ import (
// defaultMessages is the default error messages.
var defaultMessages = map[string]string{
"required": "The :attribute field is required",
"required-if": "The :attribute field is required when :other is :value",
"required-unless": "The :attribute field is required unless :other is in :values",
"required-with": "The :attribute field is required when :values is present",
"required-with-all": "The :attribute field is required when :values is present",
"required-without": "The :attribute field is required when :values is not present",
"required-without-all": "The :attribute field is required when none of :values are present",
"date": "The :attribute is not a valid date",
"date-format": "The :attribute does not match the format :format",
"email": "The :attribute must be a valid email address",
"phone": "The :attribute must be a valid phone number",
"telephone": "The :attribute must be a valid telephone number",
"required-if": "The :attribute field is required",
"required-unless": "The :attribute field is required",
"required-with": "The :attribute field is required",
"required-with-all": "The :attribute field is required",
"required-without": "The :attribute field is required",
"required-without-all": "The :attribute field is required",
"date": "The :attribute value is not a valid date",
"date-format": "The :attribute value does not match the format :format",
"email": "The :attribute value must be a valid email address",
"phone": "The :attribute value must be a valid phone number",
"telephone": "The :attribute value must be a valid telephone number",
"passport": "The :attribute value is not a valid passport format",
"password": "The :attribute value is not a valid passport format",
"password2": "The :attribute value is not a valid passport format",
"password3": "The :attribute value is not a valid passport format",
"postcode": "The :attribute value is not a valid passport format",
"resident-id": "The :attribute value is not a valid resident id number",
"bank-card": "The :attribute must be a valid bank card number",
"qq": "The :attribute must be a valid QQ number",
"ip": "The :attribute must be a valid IP address",
"ipv4": "The :attribute must be a valid IPv4 address",
"ipv6": "The :attribute must be a valid IPv6 address",
"mac": "The :attribute must be a valid MAC address",
"url": "The :attribute must be a valid URL address",
"domain": "The :attribute must be a valid domain format",
"length": "The :attribute length must be between :min and :max",
"min-length": "The :attribute length must be equal or greater than :min",
"max-length": "The :attribute length must be equal or lesser than :max",
"bank-card": "The :attribute value must be a valid bank card number",
"qq": "The :attribute value must be a valid QQ number",
"ip": "The :attribute value must be a valid IP address",
"ipv4": "The :attribute value must be a valid IPv4 address",
"ipv6": "The :attribute value must be a valid IPv6 address",
"mac": "The :attribute value must be a valid MAC address",
"url": "The :attribute value must be a valid URL address",
"domain": "The :attribute value must be a valid domain format",
"length": "The :attribute value length must be between :min and :max",
"min-length": "The :attribute value length must be equal or greater than :min",
"max-length": "The :attribute value length must be equal or lesser than :max",
"between": "The :attribute value must be between :min and :max",
"min": "The :attribute value must be equal or greater than :min",
"max": "The :attribute value must be equal or lesser than :max",
"json": "The :attribute must be a valid JSON string",
"xml": "The :attribute must be a valid XML string",
"array": "The :attribute must be an array",
"integer": "The :attribute must be an integer",
"float": "The :attribute must be a float",
"boolean": "The :attribute field must be true or false",
"json": "The :attribute value must be a valid JSON string",
"xml": "The :attribute value must be a valid XML string",
"array": "The :attribute value must be an array",
"integer": "The :attribute value must be an integer",
"float": "The :attribute value must be a float",
"boolean": "The :attribute value field must be true or false",
"same": "The :attribute value must be the same as field :other",
"different": "The :attribute value must be different from field :other",
"in": "The :attribute value is not in acceptable range",

View File

@ -12,7 +12,9 @@ import (
"strings"
)
// 对字段值长度进行检测
// checkLength checks the length rules for value.
// The length is calculated using unicode string, which means one chinese character or letter
// both has the length of 1.
func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
var (
msg = ""
@ -20,11 +22,12 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string)
valueLen = len(runeArray)
)
switch ruleKey {
// 长度范围
case "length":
array := strings.Split(ruleVal, ",")
min := 0
max := 0
var (
min = 0
max = 0
array = strings.Split(ruleVal, ",")
)
if len(array) > 0 {
if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil {
min = v
@ -46,7 +49,6 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string)
return msg
}
// 最小长度
case "min-length":
if min, err := strconv.Atoi(ruleVal); err == nil {
if valueLen < min {
@ -61,7 +63,6 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string)
msg = "校验参数[" + ruleVal + "]应当为整数类型"
}
// 最大长度
case "max-length":
if max, err := strconv.Atoi(ruleVal); err == nil {
if valueLen > max {

View File

@ -10,19 +10,20 @@ import (
"strings"
)
// 判断必须字段
// checkRequired checks the required rules.
func checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool {
required := false
switch ruleKey {
// 必须字段
// Required.
case "required":
required = true
// 必须字段(当任意所给定字段值与所给值相等时)
// Required unless all given field and its value are equal.
// Example: required-if: id,1,age,18
case "required-if":
required = false
array := strings.Split(ruleVal, ",")
// 必须为偶数,才能是键值对匹配
// It supports multiple field and value pairs.
if len(array)%2 == 0 {
for i := 0; i < len(array); {
tk := array[i]
@ -37,11 +38,12 @@ func checkRequired(value, ruleKey, ruleVal string, params map[string]string) boo
}
}
// 必须字段(当所给定字段值与所给值都不相等时)
// Required unless all given field and its value are not equal.
// Example: required-unless: id,1,age,18
case "required-unless":
required = true
array := strings.Split(ruleVal, ",")
// 必须为偶数,才能是键值对匹配
// It supports multiple field and value pairs.
if len(array)%2 == 0 {
for i := 0; i < len(array); {
tk := array[i]
@ -56,7 +58,8 @@ func checkRequired(value, ruleKey, ruleVal string, params map[string]string) boo
}
}
// 必须字段(当所给定任意字段值不为空时)
// Required if any of given fields are not empty.
// Example: required-with:id,name
case "required-with":
required = false
array := strings.Split(ruleVal, ",")
@ -67,7 +70,8 @@ func checkRequired(value, ruleKey, ruleVal string, params map[string]string) boo
}
}
// 必须字段(当所给定所有字段值都不为空时)
// Required if all of given fields are not empty.
// Example: required-with:id,name
case "required-with-all":
required = true
array := strings.Split(ruleVal, ",")
@ -78,7 +82,8 @@ func checkRequired(value, ruleKey, ruleVal string, params map[string]string) boo
}
}
// 必须字段(当所给定任意字段值为空时)
// Required if any of given fields are empty.
// Example: required-with:id,name
case "required-without":
required = false
array := strings.Split(ruleVal, ",")
@ -89,7 +94,8 @@ func checkRequired(value, ruleKey, ruleVal string, params map[string]string) boo
}
}
// 必须字段(当所给定所有字段值都为空时)
// Required if all of given fields are empty.
// Example: required-with:id,name
case "required-without-all":
required = true
array := strings.Split(ruleVal, ",")

View File

@ -23,9 +23,9 @@ func Test_Check(t *testing.T) {
err1 := gvalid.Check(val1, rule, nil)
err2 := gvalid.Check(val2, rule, nil)
err3 := gvalid.Check(val3, rule, nil)
t.Assert(err1, "invalid rules:abc:6,16")
t.Assert(err2, "invalid rules:abc:6,16")
t.Assert(err3, "invalid rules:abc:6,16")
t.Assert(err1, "invalid rules: abc:6,16")
t.Assert(err2, "invalid rules: abc:6,16")
t.Assert(err3, "invalid rules: abc:6,16")
})
}
@ -36,41 +36,31 @@ func Test_Required(t *testing.T) {
if m := gvalid.Check("", "required", nil); m == nil {
t.Error(m)
}
if m := gvalid.Check("", "required-if:id,1,age,18", nil, map[string]interface{}{"id": 1, "age": 19}); m == nil {
if m := gvalid.Check("", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 1, "age": 19}); m == nil {
t.Error("Required校验失败")
}
if m := gvalid.Check("", "required-if:id,1,age,18", nil, map[string]interface{}{"id": 2, "age": 19}); m != nil {
if m := gvalid.Check("", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 2, "age": 19}); m != nil {
t.Error("Required校验失败")
}
}
func Test_RequiredIf(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
rule := "required-if:100,200"
val1 := ""
val2 := "100"
val3 := "200"
err1 := gvalid.Check(val1, rule, nil)
err2 := gvalid.Check(val2, rule, nil)
err3 := gvalid.Check(val3, rule, nil)
t.Assert(err1, nil)
t.Assert(err2, nil)
t.Assert(err3, nil)
rule := "required-if:id,1,age,18"
t.AssertNE(gvalid.Check("", rule, nil, g.Map{"id": 1}), nil)
t.Assert(gvalid.Check("", rule, nil, g.Map{"id": 0}), nil)
t.AssertNE(gvalid.Check("", rule, nil, g.Map{"age": 18}), nil)
t.Assert(gvalid.Check("", rule, nil, g.Map{"age": 20}), nil)
})
}
func Test_RequiredUnless(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
rule := "required-unless:100,200"
val1 := ""
val2 := "100"
val3 := "200"
err1 := gvalid.Check(val1, rule, nil)
err2 := gvalid.Check(val2, rule, nil)
err3 := gvalid.Check(val3, rule, nil)
t.AssertNE(err1, nil)
t.Assert(err2, nil)
t.Assert(err3, nil)
rule := "required-unless:id,1,age,18"
t.Assert(gvalid.Check("", rule, nil, g.Map{"id": 1}), nil)
t.AssertNE(gvalid.Check("", rule, nil, g.Map{"id": 0}), nil)
t.Assert(gvalid.Check("", rule, nil, g.Map{"age": 18}), nil)
t.AssertNE(gvalid.Check("", rule, nil, g.Map{"age": 20}), nil)
})
}
@ -373,9 +363,9 @@ func Test_Postcode(t *testing.T) {
})
}
func Test_IDNumber(t *testing.T) {
func Test_ResidentId(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
rule := "id-number"
rule := "resident-id"
val1 := "11111111111111"
val2 := "1111111111111111"
val3 := "311128500121201"
@ -394,9 +384,9 @@ func Test_IDNumber(t *testing.T) {
})
}
func Test_LuHn(t *testing.T) {
func Test_BankCard(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
rule := "luhn"
rule := "bank-card"
val1 := "6230514630000424470"
val2 := "6230514630000424473"
err1 := gvalid.Check(val1, rule, nil)

View File

@ -16,52 +16,30 @@ import (
func Test_Map(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
rule := "ipv4"
val := "0.0.0"
msg := map[string]string{
"ipv4": "IPv4地址格式不正确",
}
err := gvalid.Check(val, rule, nil)
var (
rule = "ipv4"
val = "0.0.0"
err = gvalid.Check(val, rule, nil)
msg = map[string]string{
"ipv4": "The value must be a valid IPv4 address",
}
)
t.Assert(err.Map(), msg)
})
}
func Test_FirstString(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
rule := "ipv4"
val := "0.0.0"
err := gvalid.Check(val, rule, nil)
n := err.FirstString()
t.Assert(n, "IPv4地址格式不正确")
var (
rule = "ipv4"
val = "0.0.0"
err = gvalid.Check(val, rule, nil)
n = err.FirstString()
)
t.Assert(n, "The value must be a valid IPv4 address")
})
}
func Test_SetDefaultErrorMsgs(t *testing.T) {
rule := "integer|length:6,16"
msgs := map[string]string{
"integer": "请输入一个整数",
"length": "参数长度不对啊老铁",
}
gvalid.SetDefaultErrorMsgs(msgs)
e := gvalid.Check("6.66", rule, nil)
if e == nil || len(e.Map()) != 2 {
t.Error("规则校验失败")
} else {
if v, ok := e.Map()["integer"]; ok {
if strings.Compare(v, msgs["integer"]) != 0 {
t.Error("错误信息不匹配")
}
}
if v, ok := e.Map()["length"]; ok {
if strings.Compare(v, msgs["length"]) != 0 {
t.Error("错误信息不匹配")
}
}
}
}
func Test_CustomError1(t *testing.T) {
rule := "integer|length:6,16"
msgs := map[string]string{