mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
improve custom validation rule feature for package gvalid
This commit is contained in:
@ -7,6 +7,7 @@
|
||||
package gvalid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
"github.com/gogf/gf/net/gipv4"
|
||||
"github.com/gogf/gf/net/gipv6"
|
||||
@ -124,7 +125,6 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
// It converts value to string and then does the validation.
|
||||
var (
|
||||
// Do not trim it as the space is also part of the value.
|
||||
val = gconv.String(value)
|
||||
data = make(map[string]string)
|
||||
errorMsgArray = make(map[string]string)
|
||||
)
|
||||
@ -151,7 +151,8 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
ruleItems := strings.Split(strings.TrimSpace(rules), "|")
|
||||
for i := 0; ; {
|
||||
array := strings.Split(ruleItems[i], ":")
|
||||
if _, ok := allSupportedRules[array[0]]; !ok {
|
||||
_, ok := allSupportedRules[array[0]]
|
||||
if !ok && customRuleFuncMap[array[0]] == nil {
|
||||
if i > 0 && ruleItems[i-1][:5] == "regex" {
|
||||
ruleItems[i-1] += "|" + ruleItems[i]
|
||||
ruleItems = append(ruleItems[:i], ruleItems[i+1:]...)
|
||||
@ -170,304 +171,37 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
}
|
||||
for index := 0; index < len(ruleItems); {
|
||||
var (
|
||||
item = ruleItems[index]
|
||||
match = false
|
||||
results = ruleRegex.FindStringSubmatch(item)
|
||||
ruleKey = strings.TrimSpace(results[1])
|
||||
ruleVal = strings.TrimSpace(results[2])
|
||||
err error
|
||||
item = ruleItems[index]
|
||||
match = false
|
||||
results = ruleRegex.FindStringSubmatch(item)
|
||||
ruleKey = strings.TrimSpace(results[1])
|
||||
rulePattern = strings.TrimSpace(results[2])
|
||||
)
|
||||
if len(msgArray) > index {
|
||||
customMsgMap[ruleKey] = strings.TrimSpace(msgArray[index])
|
||||
}
|
||||
switch ruleKey {
|
||||
// Required rules.
|
||||
case
|
||||
"required",
|
||||
"required-if",
|
||||
"required-unless",
|
||||
"required-with",
|
||||
"required-with-all",
|
||||
"required-without",
|
||||
"required-without-all":
|
||||
match = checkRequired(val, ruleKey, ruleVal, data)
|
||||
|
||||
// Length rules.
|
||||
// It also supports length of unicode string.
|
||||
case
|
||||
"length",
|
||||
"min-length",
|
||||
"max-length":
|
||||
if msg := checkLength(val, ruleKey, ruleVal, customMsgMap); msg != "" {
|
||||
errorMsgArray[ruleKey] = msg
|
||||
if f, ok := customRuleFuncMap[ruleKey]; ok {
|
||||
// It checks custom validation rules with most priority.
|
||||
var (
|
||||
dataMap map[string]interface{}
|
||||
message = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
)
|
||||
if len(params) > 0 {
|
||||
dataMap = gconv.Map(params[0])
|
||||
}
|
||||
if err := f(value, message, dataMap); err != nil {
|
||||
match = false
|
||||
errorMsgArray[ruleKey] = err.Error()
|
||||
} else {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Range rules.
|
||||
case
|
||||
"min",
|
||||
"max",
|
||||
"between":
|
||||
if msg := checkRange(val, ruleKey, ruleVal, customMsgMap); msg != "" {
|
||||
errorMsgArray[ruleKey] = msg
|
||||
} else {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Custom regular expression.
|
||||
case "regex":
|
||||
// It here should check the rule as there might be special char '|' in it.
|
||||
for i := index + 1; i < len(ruleItems); i++ {
|
||||
if !gregex.IsMatchString(gSINGLE_RULE_PATTERN, ruleItems[i]) {
|
||||
ruleVal += "|" + ruleItems[i]
|
||||
index++
|
||||
}
|
||||
}
|
||||
match = gregex.IsMatchString(ruleVal, val)
|
||||
|
||||
// Date rules.
|
||||
case "date":
|
||||
// Standard date string, which must contain char '-' or '.'.
|
||||
if _, err := gtime.StrToTime(val); err == nil {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
// Date that not contains char '-' or '.'.
|
||||
if _, err := gtime.StrToTime(val, "Ymd"); err == nil {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
|
||||
// Date rule with specified format.
|
||||
case "date-format":
|
||||
if _, err := gtime.StrToTimeFormat(val, ruleVal); err == nil {
|
||||
match = true
|
||||
} else {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":format", ruleVal, -1)
|
||||
errorMsgArray[ruleKey] = msg
|
||||
}
|
||||
|
||||
// Values of two fields should be equal as string.
|
||||
case "same":
|
||||
if v, ok := data[ruleVal]; ok {
|
||||
if strings.Compare(val, v) == 0 {
|
||||
match = true
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":field", ruleVal, -1)
|
||||
errorMsgArray[ruleKey] = msg
|
||||
}
|
||||
|
||||
// Values of two fields should not be equal as string.
|
||||
case "different":
|
||||
match = true
|
||||
if v, ok := data[ruleVal]; ok {
|
||||
if strings.Compare(val, v) == 0 {
|
||||
match = false
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":field", ruleVal, -1)
|
||||
errorMsgArray[ruleKey] = msg
|
||||
}
|
||||
|
||||
// Field value should be in range of.
|
||||
case "in":
|
||||
array := strings.Split(ruleVal, ",")
|
||||
for _, v := range array {
|
||||
if strings.Compare(val, strings.TrimSpace(v)) == 0 {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Field value should not be in range of.
|
||||
case "not-in":
|
||||
match = true
|
||||
array := strings.Split(ruleVal, ",")
|
||||
for _, v := range array {
|
||||
if strings.Compare(val, strings.TrimSpace(v)) == 0 {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Phone format validation.
|
||||
// 1. China Mobile:
|
||||
// 134, 135, 136, 137, 138, 139, 150, 151, 152, 157, 158, 159, 182, 183, 184, 187, 188,
|
||||
// 178(4G), 147(Net);
|
||||
//
|
||||
// 2. China Unicom:
|
||||
// 130, 131, 132, 155, 156, 185, 186 ,176(4G), 145(Net), 175
|
||||
//
|
||||
// 3. China Telecom:
|
||||
// 133, 153, 180, 181, 189, 177(4G)
|
||||
//
|
||||
// 4. Satelite:
|
||||
// 1349
|
||||
//
|
||||
// 5. Virtual:
|
||||
// 170, 173
|
||||
//
|
||||
// 6. 2018:
|
||||
// 16x, 19x
|
||||
case "phone":
|
||||
match = gregex.IsMatchString(`^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$`, val)
|
||||
|
||||
// Telephone number:
|
||||
// "XXXX-XXXXXXX"
|
||||
// "XXXX-XXXXXXXX"
|
||||
// "XXX-XXXXXXX"
|
||||
// "XXX-XXXXXXXX"
|
||||
// "XXXXXXX"
|
||||
// "XXXXXXXX"
|
||||
case "telephone":
|
||||
match = gregex.IsMatchString(`^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, val)
|
||||
|
||||
// QQ number: from 10000.
|
||||
case "qq":
|
||||
match = gregex.IsMatchString(`^[1-9][0-9]{4,}$`, val)
|
||||
|
||||
// Postcode number.
|
||||
case "postcode":
|
||||
match = gregex.IsMatchString(`^\d{6}$`, val)
|
||||
|
||||
// China resident id number.
|
||||
//
|
||||
// xxxxxx yyyy MM dd 375 0 十八位
|
||||
// xxxxxx yy MM dd 75 0 十五位
|
||||
//
|
||||
// 地区: [1-9]\d{5}
|
||||
// 年的前两位:(18|19|([23]\d)) 1800-2399
|
||||
// 年的后两位:\d{2}
|
||||
// 月份: ((0[1-9])|(10|11|12))
|
||||
// 天数: (([0-2][1-9])|10|20|30|31) 闰年不能禁止29+
|
||||
//
|
||||
// 三位顺序码:\d{3}
|
||||
// 两位顺序码:\d{2}
|
||||
// 校验码: [0-9Xx]
|
||||
//
|
||||
// 十八位:^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
|
||||
// 十五位:^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$
|
||||
//
|
||||
// 总:
|
||||
// (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
|
||||
case "resident-id":
|
||||
match = checkResidentId(val)
|
||||
|
||||
// Bank card number using LUHN algorithm.
|
||||
case "bank-card":
|
||||
match = checkLuHn(val)
|
||||
|
||||
// Universal passport format rule:
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Json.
|
||||
case "json":
|
||||
if _, err := gjson.Decode([]byte(val)); err == nil {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Integer.
|
||||
case "integer":
|
||||
if _, err := strconv.Atoi(val); err == nil {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Float.
|
||||
case "float":
|
||||
if _, err := strconv.ParseFloat(val, 10); err == nil {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Boolean(1,true,on,yes:true | 0,false,off,no,"":false).
|
||||
case "boolean":
|
||||
match = false
|
||||
if _, ok := boolMap[strings.ToLower(val)]; ok {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Email.
|
||||
case "email":
|
||||
match = gregex.IsMatchString(`^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)+$`, val)
|
||||
|
||||
// URL
|
||||
case "url":
|
||||
match = gregex.IsMatchString(`(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]`, val)
|
||||
|
||||
// Domain
|
||||
case "domain":
|
||||
match = gregex.IsMatchString(`^([0-9a-zA-Z][0-9a-zA-Z\-]{0,62}\.)+([a-zA-Z]{0,62})$`, val)
|
||||
|
||||
// IP(IPv4/IPv6).
|
||||
case "ip":
|
||||
match = gipv4.Validate(val) || gipv6.Validate(val)
|
||||
|
||||
// IPv4.
|
||||
case "ipv4":
|
||||
match = gipv4.Validate(val)
|
||||
|
||||
// IPv6.
|
||||
case "ipv6":
|
||||
match = gipv6.Validate(val)
|
||||
|
||||
// MAC.
|
||||
case "mac":
|
||||
match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, val)
|
||||
|
||||
default:
|
||||
// Custom validation rules.
|
||||
if f, ok := customRuleFuncMap[ruleKey]; ok {
|
||||
var (
|
||||
dataMap map[string]interface{}
|
||||
message = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
)
|
||||
if len(params) > 0 {
|
||||
dataMap = gconv.Map(params[0])
|
||||
}
|
||||
if err := f(value, message, dataMap); err != nil {
|
||||
match = false
|
||||
errorMsgArray[ruleKey] = err.Error()
|
||||
} else {
|
||||
match = true
|
||||
}
|
||||
} else {
|
||||
errorMsgArray[ruleKey] = "Invalid rule name: " + ruleKey
|
||||
} else {
|
||||
// It checks build-in validation rules if there's no custom rule.
|
||||
match, err = doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap)
|
||||
if !match && err != nil {
|
||||
errorMsgArray[ruleKey] = err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,3 +222,290 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doCheckBuildInRules(
|
||||
index int,
|
||||
value interface{},
|
||||
ruleKey string,
|
||||
rulePattern string,
|
||||
ruleItems []string,
|
||||
dataMap map[string]string,
|
||||
customMsgMap map[string]string,
|
||||
) (match bool, err error) {
|
||||
valueStr := gconv.String(value)
|
||||
switch ruleKey {
|
||||
// Required rules.
|
||||
case
|
||||
"required",
|
||||
"required-if",
|
||||
"required-unless",
|
||||
"required-with",
|
||||
"required-with-all",
|
||||
"required-without",
|
||||
"required-without-all":
|
||||
match = checkRequired(valueStr, ruleKey, rulePattern, dataMap)
|
||||
|
||||
// Length rules.
|
||||
// It also supports length of unicode string.
|
||||
case
|
||||
"length",
|
||||
"min-length",
|
||||
"max-length":
|
||||
if msg := checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
|
||||
return match, errors.New(msg)
|
||||
} else {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Range rules.
|
||||
case
|
||||
"min",
|
||||
"max",
|
||||
"between":
|
||||
if msg := checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
|
||||
return match, errors.New(msg)
|
||||
} else {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Custom regular expression.
|
||||
case "regex":
|
||||
// It here should check the rule as there might be special char '|' in it.
|
||||
for i := index + 1; i < len(ruleItems); i++ {
|
||||
if !gregex.IsMatchString(gSINGLE_RULE_PATTERN, ruleItems[i]) {
|
||||
rulePattern += "|" + ruleItems[i]
|
||||
index++
|
||||
}
|
||||
}
|
||||
match = gregex.IsMatchString(rulePattern, valueStr)
|
||||
|
||||
// Date rules.
|
||||
case "date":
|
||||
// Standard date string, which must contain char '-' or '.'.
|
||||
if _, err := gtime.StrToTime(valueStr); err == nil {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
// Date that not contains char '-' or '.'.
|
||||
if _, err := gtime.StrToTime(valueStr, "Ymd"); err == nil {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
|
||||
// Date rule with specified format.
|
||||
case "date-format":
|
||||
if _, err := gtime.StrToTimeFormat(valueStr, rulePattern); err == nil {
|
||||
match = true
|
||||
} else {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":format", rulePattern, -1)
|
||||
return match, errors.New(msg)
|
||||
}
|
||||
|
||||
// Values of two fields should be equal as string.
|
||||
case "same":
|
||||
if v, ok := dataMap[rulePattern]; ok {
|
||||
if strings.Compare(valueStr, v) == 0 {
|
||||
match = true
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":field", rulePattern, -1)
|
||||
return match, errors.New(msg)
|
||||
}
|
||||
|
||||
// Values of two fields should not be equal as string.
|
||||
case "different":
|
||||
match = true
|
||||
if v, ok := dataMap[rulePattern]; ok {
|
||||
if strings.Compare(valueStr, v) == 0 {
|
||||
match = false
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":field", rulePattern, -1)
|
||||
return match, errors.New(msg)
|
||||
}
|
||||
|
||||
// Field value should be in range of.
|
||||
case "in":
|
||||
array := strings.Split(rulePattern, ",")
|
||||
for _, v := range array {
|
||||
if strings.Compare(valueStr, strings.TrimSpace(v)) == 0 {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Field value should not be in range of.
|
||||
case "not-in":
|
||||
match = true
|
||||
array := strings.Split(rulePattern, ",")
|
||||
for _, v := range array {
|
||||
if strings.Compare(valueStr, strings.TrimSpace(v)) == 0 {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Phone format validation.
|
||||
// 1. China Mobile:
|
||||
// 134, 135, 136, 137, 138, 139, 150, 151, 152, 157, 158, 159, 182, 183, 184, 187, 188,
|
||||
// 178(4G), 147(Net);
|
||||
//
|
||||
// 2. China Unicom:
|
||||
// 130, 131, 132, 155, 156, 185, 186 ,176(4G), 145(Net), 175
|
||||
//
|
||||
// 3. China Telecom:
|
||||
// 133, 153, 180, 181, 189, 177(4G)
|
||||
//
|
||||
// 4. Satelite:
|
||||
// 1349
|
||||
//
|
||||
// 5. Virtual:
|
||||
// 170, 173
|
||||
//
|
||||
// 6. 2018:
|
||||
// 16x, 19x
|
||||
case "phone":
|
||||
match = gregex.IsMatchString(`^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$`, valueStr)
|
||||
|
||||
// Telephone number:
|
||||
// "XXXX-XXXXXXX"
|
||||
// "XXXX-XXXXXXXX"
|
||||
// "XXX-XXXXXXX"
|
||||
// "XXX-XXXXXXXX"
|
||||
// "XXXXXXX"
|
||||
// "XXXXXXXX"
|
||||
case "telephone":
|
||||
match = gregex.IsMatchString(`^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, valueStr)
|
||||
|
||||
// QQ number: from 10000.
|
||||
case "qq":
|
||||
match = gregex.IsMatchString(`^[1-9][0-9]{4,}$`, valueStr)
|
||||
|
||||
// Postcode number.
|
||||
case "postcode":
|
||||
match = gregex.IsMatchString(`^\d{6}$`, valueStr)
|
||||
|
||||
// China resident id number.
|
||||
//
|
||||
// xxxxxx yyyy MM dd 375 0 十八位
|
||||
// xxxxxx yy MM dd 75 0 十五位
|
||||
//
|
||||
// 地区: [1-9]\d{5}
|
||||
// 年的前两位:(18|19|([23]\d)) 1800-2399
|
||||
// 年的后两位:\d{2}
|
||||
// 月份: ((0[1-9])|(10|11|12))
|
||||
// 天数: (([0-2][1-9])|10|20|30|31) 闰年不能禁止29+
|
||||
//
|
||||
// 三位顺序码:\d{3}
|
||||
// 两位顺序码:\d{2}
|
||||
// 校验码: [0-9Xx]
|
||||
//
|
||||
// 十八位:^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
|
||||
// 十五位:^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$
|
||||
//
|
||||
// 总:
|
||||
// (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
|
||||
case "resident-id":
|
||||
match = checkResidentId(valueStr)
|
||||
|
||||
// Bank card number using LUHN algorithm.
|
||||
case "bank-card":
|
||||
match = checkLuHn(valueStr)
|
||||
|
||||
// Universal passport format rule:
|
||||
// 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}$`, valueStr)
|
||||
|
||||
// Universal password format rule1:
|
||||
// Containing any visible chars, length between 6 and 18.
|
||||
case "password":
|
||||
match = gregex.IsMatchString(`^[\w\S]{6,18}$`, valueStr)
|
||||
|
||||
// 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}$`, valueStr) &&
|
||||
gregex.IsMatchString(`[a-z]+`, valueStr) &&
|
||||
gregex.IsMatchString(`[A-Z]+`, valueStr) &&
|
||||
gregex.IsMatchString(`\d+`, valueStr) {
|
||||
match = true
|
||||
}
|
||||
|
||||
// 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}$`, valueStr) &&
|
||||
gregex.IsMatchString(`[a-z]+`, valueStr) &&
|
||||
gregex.IsMatchString(`[A-Z]+`, valueStr) &&
|
||||
gregex.IsMatchString(`\d+`, valueStr) &&
|
||||
gregex.IsMatchString(`[^a-zA-Z0-9]+`, valueStr) {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Json.
|
||||
case "json":
|
||||
if _, err := gjson.Decode([]byte(valueStr)); err == nil {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Integer.
|
||||
case "integer":
|
||||
if _, err := strconv.Atoi(valueStr); err == nil {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Float.
|
||||
case "float":
|
||||
if _, err := strconv.ParseFloat(valueStr, 10); err == nil {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Boolean(1,true,on,yes:true | 0,false,off,no,"":false).
|
||||
case "boolean":
|
||||
match = false
|
||||
if _, ok := boolMap[strings.ToLower(valueStr)]; ok {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Email.
|
||||
case "email":
|
||||
match = gregex.IsMatchString(`^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)+$`, valueStr)
|
||||
|
||||
// URL
|
||||
case "url":
|
||||
match = gregex.IsMatchString(`(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]`, valueStr)
|
||||
|
||||
// Domain
|
||||
case "domain":
|
||||
match = gregex.IsMatchString(`^([0-9a-zA-Z][0-9a-zA-Z\-]{0,62}\.)+([a-zA-Z]{0,62})$`, valueStr)
|
||||
|
||||
// IP(IPv4/IPv6).
|
||||
case "ip":
|
||||
match = gipv4.Validate(valueStr) || gipv6.Validate(valueStr)
|
||||
|
||||
// IPv4.
|
||||
case "ipv4":
|
||||
match = gipv4.Validate(valueStr)
|
||||
|
||||
// IPv6.
|
||||
case "ipv6":
|
||||
match = gipv6.Validate(valueStr)
|
||||
|
||||
// MAC.
|
||||
case "mac":
|
||||
match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, valueStr)
|
||||
|
||||
default:
|
||||
return match, errors.New("Invalid rule name: " + ruleKey)
|
||||
}
|
||||
return match, nil
|
||||
}
|
||||
|
||||
@ -6,10 +6,6 @@
|
||||
|
||||
package gvalid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RuleFunc is the custom function for data validation.
|
||||
// The parameter <value> specifies the value for this rule to validate.
|
||||
// The parameter <message> specifies the custom error message or configured i18n message for this rule.
|
||||
@ -25,10 +21,11 @@ var (
|
||||
// RegisterRule registers custom validation rule and function for package.
|
||||
// It returns error if there's already the same rule registered previously.
|
||||
func RegisterRule(rule string, f RuleFunc) error {
|
||||
if _, ok := allSupportedRules[rule]; ok {
|
||||
return fmt.Errorf(`validation rule "%s" is already registered`, rule)
|
||||
}
|
||||
allSupportedRules[rule] = struct{}{}
|
||||
customRuleFuncMap[rule] = f
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRule deletes custom defined validation rule and its function from global package.
|
||||
func DeleteRule(rule string) {
|
||||
delete(customRuleFuncMap, rule)
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func ExampleCheckMap() {
|
||||
@ -71,9 +73,9 @@ func ExampleCheckStruct() {
|
||||
Size: 10,
|
||||
}
|
||||
err := gvalid.CheckStruct(obj, nil)
|
||||
fmt.Println(err)
|
||||
fmt.Println(err == nil)
|
||||
// Output:
|
||||
// <nil>
|
||||
// true
|
||||
}
|
||||
|
||||
// Empty pointer attribute.
|
||||
@ -88,9 +90,9 @@ func ExampleCheckStruct2() {
|
||||
Size: 10,
|
||||
}
|
||||
err := gvalid.CheckStruct(obj, nil)
|
||||
fmt.Println(err)
|
||||
fmt.Println(err == nil)
|
||||
// Output:
|
||||
// <nil>
|
||||
// true
|
||||
}
|
||||
|
||||
// Empty integer attribute.
|
||||
@ -138,6 +140,50 @@ func ExampleRegisterRule() {
|
||||
}
|
||||
err := gvalid.CheckStruct(user, nil)
|
||||
fmt.Println(err.Error())
|
||||
// Output:
|
||||
// May Output:
|
||||
// 用户名称已被占用
|
||||
}
|
||||
|
||||
func ExampleRegisterRule_OverwriteRequired() {
|
||||
rule := "required"
|
||||
gvalid.RegisterRule(rule, func(value interface{}, message string, params map[string]interface{}) error {
|
||||
reflectValue := reflect.ValueOf(value)
|
||||
if reflectValue.Kind() == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
isEmpty := false
|
||||
switch reflectValue.Kind() {
|
||||
case reflect.Bool:
|
||||
isEmpty = !reflectValue.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
isEmpty = reflectValue.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
isEmpty = reflectValue.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
isEmpty = math.Float64bits(reflectValue.Float()) == 0
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
c := reflectValue.Complex()
|
||||
isEmpty = math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
|
||||
case reflect.String, reflect.Map, reflect.Array, reflect.Slice:
|
||||
isEmpty = reflectValue.Len() == 0
|
||||
}
|
||||
if isEmpty {
|
||||
return errors.New(message)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
fmt.Println(gvalid.Check("", "required", "It's required"))
|
||||
fmt.Println(gvalid.Check([]string{}, "required", "It's required"))
|
||||
fmt.Println(gvalid.Check(map[string]int{}, "required", "It's required"))
|
||||
gvalid.DeleteRule(rule)
|
||||
fmt.Println("rule deleted")
|
||||
fmt.Println(gvalid.Check("", "required", "It's required"))
|
||||
fmt.Println(gvalid.Check([]string{}, "required", "It's required"))
|
||||
fmt.Println(gvalid.Check(map[string]int{}, "required", "It's required"))
|
||||
// Output:
|
||||
// It's required
|
||||
// It's required
|
||||
// It's required
|
||||
// rule deleted
|
||||
// It's required
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user