mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
improve package gvalid
This commit is contained in:
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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]
|
||||
}
|
||||
}
|
||||
// 开始执行校验: 以校验规则作为基础进行遍历校验
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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, ",")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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{
|
||||
|
||||
Reference in New Issue
Block a user