2017-12-29 16:03:30 +08:00
|
|
|
|
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
|
|
|
|
// You can obtain one at https://gitee.com/johng/gf.
|
|
|
|
|
//
|
2017-12-28 17:28:32 +08:00
|
|
|
|
// 通用数据验证工具
|
|
|
|
|
// 本来打算取名gvalidator的,名字太长了,缩写一下
|
2017-12-29 00:03:32 +08:00
|
|
|
|
/*
|
|
|
|
|
参考:https://laravel.com/docs/5.5/validation#available-validation-rules
|
|
|
|
|
规则如下:
|
2017-12-29 15:42:42 +08:00
|
|
|
|
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 说明:公民身份证号码
|
|
|
|
|
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(长度参数为整形)
|
|
|
|
|
min-length 格式:min-length:min 说明:参数长度最小为min(长度参数为整形)
|
|
|
|
|
max-length 格式:max-length:max 说明:参数长度最大为max(长度参数为整形)
|
|
|
|
|
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
|
2017-12-29 00:03:32 +08:00
|
|
|
|
*/
|
2017-12-31 18:19:58 +08:00
|
|
|
|
|
2017-12-28 17:28:32 +08:00
|
|
|
|
package gvalid
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"strings"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strconv"
|
|
|
|
|
"gitee.com/johng/gf/g/os/gtime"
|
|
|
|
|
"gitee.com/johng/gf/g/util/gregx"
|
|
|
|
|
"gitee.com/johng/gf/g/encoding/gjson"
|
2017-12-29 15:42:42 +08:00
|
|
|
|
"gitee.com/johng/gf/g/container/gmap"
|
|
|
|
|
"gitee.com/johng/gf/g/net/gipv6"
|
|
|
|
|
"gitee.com/johng/gf/g/net/gipv4"
|
2017-12-28 17:28:32 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 默认规则校验错误消息(可以通过接口自定义错误消息)
|
|
|
|
|
var defaultMessages = map[string]string {
|
2017-12-29 15:42:42 +08:00
|
|
|
|
"required" : "字段不能为空",
|
|
|
|
|
"required-if" : "字段不能为空",
|
|
|
|
|
"required-unless" : "字段不能为空",
|
|
|
|
|
"required-with" : "字段不能为空",
|
|
|
|
|
"required-with-all" : "字段不能为空",
|
|
|
|
|
"required-without" : "字段不能为空",
|
|
|
|
|
"required-without-all" : "字段不能为空",
|
|
|
|
|
"date" : "日期格式不正确",
|
|
|
|
|
"date-format" : "日期格式不正确",
|
|
|
|
|
"email" : "邮箱地址格式不正确",
|
|
|
|
|
"phone" : "手机号码格式不正确",
|
|
|
|
|
"telephone" : "电话号码格式不正确",
|
|
|
|
|
"passport" : "账号格式不合法,必需以字母开头,只能包含字母、数字和下划线,长度在6~18之间",
|
|
|
|
|
"password" : "密码格式不合法,密码格式为任意6-18位的可见字符",
|
|
|
|
|
"password2" : "密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母和数字",
|
|
|
|
|
"password3" : "密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母、数字和特殊字符",
|
|
|
|
|
"postcode" : "邮政编码不正确",
|
|
|
|
|
"id-number" : "身份证号码不正确",
|
|
|
|
|
"qq" : "QQ号码格式不正确",
|
|
|
|
|
"ip" : "IP地址格式不正确",
|
|
|
|
|
"ipv4" : "IPv4地址格式不正确",
|
|
|
|
|
"ipv6" : "IPv6地址格式不正确",
|
|
|
|
|
"mac" : "MAC地址格式不正确",
|
|
|
|
|
"url" : "URL地址格式不正确",
|
|
|
|
|
"domain" : "域名格式不正确",
|
|
|
|
|
"length" : "字段长度为:min到:max个字符",
|
|
|
|
|
"min-length" : "字段最小长度为:min",
|
|
|
|
|
"max-length" : "字段最大长度为:max",
|
|
|
|
|
"between" : "字段大小为:min到:max",
|
|
|
|
|
"min" : "字段最小值为:min",
|
|
|
|
|
"max" : "字段最大值为:max",
|
|
|
|
|
"json" : "字段应当为JSON格式",
|
|
|
|
|
"xml" : "字段应当为XML格式",
|
|
|
|
|
"array" : "字段应当为数组",
|
|
|
|
|
"integer" : "字段应当为整数",
|
|
|
|
|
"float" : "字段应当为浮点数",
|
|
|
|
|
"boolean" : "字段应当为布尔值",
|
|
|
|
|
"same" : "字段值不合法",
|
|
|
|
|
"different" : "字段值不合法",
|
|
|
|
|
"in" : "字段值不合法",
|
|
|
|
|
"not-in" : "字段值不合法",
|
|
|
|
|
"regex" : "字段值不合法",
|
2017-12-28 17:28:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 15:42:42 +08:00
|
|
|
|
const (
|
|
|
|
|
gSINGLE_RULE_PATTERN = `^([\w-]+):{0,1}(.*)` // 单条规则匹配正则
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 默认错误消息管理对象(并发安全)
|
|
|
|
|
var errorMsgMap = gmap.NewStringStringMap()
|
|
|
|
|
|
|
|
|
|
// 单规则正则对象,这里使用包内部变量存储,不需要多次解析
|
|
|
|
|
var ruleRegex, _ = regexp.Compile(gSINGLE_RULE_PATTERN)
|
|
|
|
|
|
|
|
|
|
// 初始化错误消息管理对象
|
|
|
|
|
func init() {
|
|
|
|
|
errorMsgMap.BatchSet(defaultMessages)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 替换默认的错误提示为指定的自定义提示
|
|
|
|
|
// 主要作用:
|
|
|
|
|
// 1、便于多语言错误提示设置;
|
|
|
|
|
// 2、默认错误提示信息不满意;
|
|
|
|
|
func SetDefaultErrorMsgs(msgs map[string]string) {
|
|
|
|
|
errorMsgMap.BatchSet(msgs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 判断必须字段
|
|
|
|
|
func checkRequired(value, rulekey, ruleval string, params map[string]string) bool {
|
|
|
|
|
required := false
|
|
|
|
|
switch rulekey {
|
|
|
|
|
// 必须字段
|
|
|
|
|
case "required":
|
|
|
|
|
required = true
|
|
|
|
|
|
|
|
|
|
// 必须字段(当任意所给定字段值与所给值相等时)
|
|
|
|
|
case "required-if":
|
|
|
|
|
required = false
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
// 必须为偶数,才能是键值对匹配
|
|
|
|
|
if len(array)%2 == 0 {
|
|
|
|
|
for i := 0; i < len(array); {
|
|
|
|
|
tk := array[i]
|
|
|
|
|
tv := array[i+1]
|
|
|
|
|
if v, ok := params[tk]; ok {
|
|
|
|
|
if strings.Compare(tv, v) == 0 {
|
2017-12-29 00:03:32 +08:00
|
|
|
|
required = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-29 15:42:42 +08:00
|
|
|
|
i += 2
|
2017-12-29 00:03:32 +08:00
|
|
|
|
}
|
2017-12-29 15:42:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 必须字段(当所给定字段值与所给值都不相等时)
|
|
|
|
|
case "required-unless":
|
|
|
|
|
required = true
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
// 必须为偶数,才能是键值对匹配
|
|
|
|
|
if len(array)%2 == 0 {
|
|
|
|
|
for i := 0; i < len(array); {
|
|
|
|
|
tk := array[i]
|
|
|
|
|
tv := array[i+1]
|
|
|
|
|
if v, ok := params[tk]; ok {
|
|
|
|
|
if strings.Compare(tv, v) == 0 {
|
2017-12-29 00:03:32 +08:00
|
|
|
|
required = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-29 15:42:42 +08:00
|
|
|
|
i += 2
|
2017-12-29 00:03:32 +08:00
|
|
|
|
}
|
2017-12-29 15:42:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 必须字段(当所给定任意字段值不为空时)
|
|
|
|
|
case "required-with":
|
|
|
|
|
required = false
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
for i := 0; i < len(array); i++ {
|
|
|
|
|
if v, ok := params[array[i]]; ok {
|
|
|
|
|
if v != "" {
|
|
|
|
|
required = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 必须字段(当所给定所有字段值都不为空时)
|
|
|
|
|
case "required-with-all":
|
|
|
|
|
required = true
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
for i := 0; i < len(array); i++ {
|
|
|
|
|
if v, ok := params[array[i]]; ok {
|
|
|
|
|
if v == "" {
|
|
|
|
|
required = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 必须字段(当所给定任意字段值为空时)
|
|
|
|
|
case "required-without":
|
|
|
|
|
required = false
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
for i := 0; i < len(array); i++ {
|
|
|
|
|
if v, ok := params[array[i]]; ok {
|
|
|
|
|
if v == "" {
|
|
|
|
|
required = true
|
|
|
|
|
break
|
|
|
|
|
}
|
2017-12-29 00:03:32 +08:00
|
|
|
|
}
|
2017-12-29 15:42:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 必须字段(当所给定所有字段值都为空时)
|
|
|
|
|
case "required-without-all":
|
|
|
|
|
required = true
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
for i := 0; i < len(array); i++ {
|
|
|
|
|
if v, ok := params[array[i]]; ok {
|
|
|
|
|
if v != "" {
|
|
|
|
|
required = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if required {
|
|
|
|
|
return !(value == "")
|
|
|
|
|
} else {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 16:56:49 +08:00
|
|
|
|
// 对字段值长度进行检测
|
|
|
|
|
func checkLength(value, rulekey, ruleval string, cmsgs map[string]string) string {
|
|
|
|
|
msg := ""
|
|
|
|
|
switch rulekey {
|
|
|
|
|
// 长度范围
|
|
|
|
|
case "length":
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
min := 0
|
|
|
|
|
max := 0
|
|
|
|
|
if len(array) > 0 {
|
|
|
|
|
if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil {
|
|
|
|
|
min = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(array) > 1 {
|
|
|
|
|
if v, err := strconv.Atoi(strings.TrimSpace(array[1])); err == nil {
|
|
|
|
|
max = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(value) < min || len(value) > max {
|
|
|
|
|
if v, ok := cmsgs[rulekey]; !ok {
|
|
|
|
|
msg = errorMsgMap.Get(rulekey)
|
|
|
|
|
} else {
|
|
|
|
|
msg = v
|
|
|
|
|
}
|
|
|
|
|
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
|
|
|
|
|
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
|
|
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最小长度
|
|
|
|
|
case "min-length":
|
|
|
|
|
if min, err := strconv.Atoi(ruleval); err == nil {
|
|
|
|
|
if len(value) < min {
|
|
|
|
|
if v, ok := cmsgs[rulekey]; !ok {
|
|
|
|
|
msg = errorMsgMap.Get(rulekey)
|
|
|
|
|
} else {
|
|
|
|
|
msg = v
|
|
|
|
|
}
|
|
|
|
|
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
msg = "校验参数[" + ruleval + "]应当为整数类型"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最大长度
|
|
|
|
|
case "max-length":
|
|
|
|
|
if max, err := strconv.Atoi(ruleval); err == nil {
|
|
|
|
|
if len(value) > max {
|
|
|
|
|
if v, ok := cmsgs[rulekey]; !ok {
|
|
|
|
|
msg = errorMsgMap.Get(rulekey)
|
|
|
|
|
} else {
|
|
|
|
|
msg = v
|
|
|
|
|
}
|
|
|
|
|
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
msg = "校验参数[" + ruleval + "]应当为整数类型"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 对字段值大小进行检测
|
|
|
|
|
func checkSize(value, rulekey, ruleval string, cmsgs map[string]string) string {
|
|
|
|
|
msg := ""
|
|
|
|
|
switch rulekey {
|
|
|
|
|
// 大小范围
|
|
|
|
|
case "between":
|
|
|
|
|
array := strings.Split(ruleval, ",")
|
|
|
|
|
min := float64(0)
|
|
|
|
|
max := float64(0)
|
|
|
|
|
if len(array) > 0 {
|
|
|
|
|
if v, err := strconv.ParseFloat(strings.TrimSpace(array[0]), 10); err == nil {
|
|
|
|
|
min = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(array) > 1 {
|
|
|
|
|
if v, err := strconv.ParseFloat(strings.TrimSpace(array[1]), 10); err == nil {
|
|
|
|
|
max = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if v, err := strconv.ParseFloat(value, 10); err == nil {
|
|
|
|
|
if v < min || v > max {
|
|
|
|
|
if v, ok := cmsgs[rulekey]; !ok {
|
|
|
|
|
msg = errorMsgMap.Get(rulekey)
|
|
|
|
|
} else {
|
|
|
|
|
msg = v
|
|
|
|
|
}
|
|
|
|
|
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
|
|
|
|
|
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
msg = "输入参数[" + value + "]应当为数字类型"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最小值
|
|
|
|
|
case "min":
|
|
|
|
|
if min, err := strconv.ParseFloat(ruleval, 10); err == nil {
|
|
|
|
|
if v, err := strconv.ParseFloat(value, 10); err == nil {
|
|
|
|
|
if v < min {
|
|
|
|
|
msg, ok := cmsgs[rulekey]
|
|
|
|
|
if !ok {
|
|
|
|
|
msg = errorMsgMap.Get(rulekey)
|
|
|
|
|
}
|
|
|
|
|
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
msg = "输入参数[" + value + "]应当为数字类型"
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
msg = "校验参数[" + ruleval + "]应当为数字类型"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最大值
|
|
|
|
|
case "max":
|
|
|
|
|
if max, err := strconv.ParseFloat(ruleval, 10); err == nil {
|
|
|
|
|
if v, err := strconv.ParseFloat(value, 10); err == nil {
|
|
|
|
|
if v > max {
|
|
|
|
|
msg, ok := cmsgs[rulekey]
|
|
|
|
|
if !ok {
|
|
|
|
|
msg = errorMsgMap.Get(rulekey)
|
|
|
|
|
}
|
|
|
|
|
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
msg = "输入参数[" + value + "]应当为数字类型"
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
msg = "校验参数[" + ruleval + "]应当为数字类型"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检测键值对Map,注意返回参数是一个2维的关联数组,第一维键名为参数键名,第二维为带有错误的校验规则名称,值为错误信息
|
|
|
|
|
func CheckMap(kvmap map[string]string, rules map[string]string, msgs map[string]interface{}) map[string]map[string]string {
|
|
|
|
|
emsgs := make(map[string]map[string]string)
|
|
|
|
|
for key, value := range kvmap {
|
|
|
|
|
if rule, ok := rules[key]; ok {
|
|
|
|
|
msg, _ := msgs[key]
|
|
|
|
|
if m := Check(value, rule, msg, kvmap); m != nil {
|
|
|
|
|
if _, ok := emsgs[key]; !ok {
|
|
|
|
|
emsgs[key] = make(map[string]string)
|
|
|
|
|
}
|
|
|
|
|
for k, v := range m {
|
|
|
|
|
emsgs[key][k] = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(emsgs) > 0 {
|
|
|
|
|
return emsgs
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 15:42:42 +08:00
|
|
|
|
// 检测单条数据的规则,其中values参数为非必须参数,可以传递所有的校验参数进来,进行多参数对比(部分校验规则需要)
|
|
|
|
|
// msgs为自定义错误信息,由于同一条数据的校验规则可能存在多条,为方便调用,参数类型支持string/map[string]string,允许传递多个自定义的错误信息,如果类型为string,那么中间使用"|"符号分隔多个自定义错误
|
|
|
|
|
// values参数为表单联合校验参数,对于需要联合校验的规则有效,如:required-*、same、different
|
|
|
|
|
func Check(value, rules string, msgs interface{}, values...map[string]string) map[string]string {
|
|
|
|
|
value = strings.TrimSpace(value)
|
|
|
|
|
params := make(map[string]string)
|
|
|
|
|
errmsgs := make(map[string]string)
|
|
|
|
|
if len(values) > 0 {
|
|
|
|
|
params = values[0]
|
|
|
|
|
}
|
|
|
|
|
// 自定义错误消息处理
|
|
|
|
|
list := make([]string, 0)
|
|
|
|
|
cmsgs := make(map[string]string)
|
|
|
|
|
switch msgs.(type) {
|
|
|
|
|
case map[string]string:
|
|
|
|
|
cmsgs = msgs.(map[string]string)
|
|
|
|
|
case string:
|
|
|
|
|
list = strings.Split(msgs.(string), "|")
|
|
|
|
|
}
|
|
|
|
|
items := strings.Split(strings.TrimSpace(rules), "|")
|
|
|
|
|
for index := 0; index < len(items); {
|
|
|
|
|
item := items[index]
|
|
|
|
|
results := ruleRegex.FindStringSubmatch(item)
|
|
|
|
|
rulekey := strings.TrimSpace(results[1])
|
|
|
|
|
ruleval := strings.TrimSpace(results[2])
|
|
|
|
|
match := false
|
|
|
|
|
if len(list) > index {
|
|
|
|
|
cmsgs[rulekey] = strings.TrimSpace(list[index])
|
|
|
|
|
}
|
|
|
|
|
switch rulekey {
|
|
|
|
|
// 必须字段
|
|
|
|
|
case "required": fallthrough
|
|
|
|
|
case "required-if": fallthrough
|
|
|
|
|
case "required-unless": fallthrough
|
|
|
|
|
case "required-with": fallthrough
|
|
|
|
|
case "required-with-all": fallthrough
|
|
|
|
|
case "required-without": fallthrough
|
|
|
|
|
case "required-without-all":
|
|
|
|
|
match = checkRequired(value, rulekey, ruleval, params)
|
|
|
|
|
|
2017-12-29 16:56:49 +08:00
|
|
|
|
// 长度范围
|
|
|
|
|
case "length": fallthrough
|
|
|
|
|
case "min-length": fallthrough
|
|
|
|
|
case "max-length":
|
|
|
|
|
if msg := checkLength(value, rulekey, ruleval, cmsgs); msg != "" {
|
|
|
|
|
errmsgs[rulekey] = msg
|
|
|
|
|
} else {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 大小范围
|
|
|
|
|
case "min": fallthrough
|
|
|
|
|
case "max": fallthrough
|
|
|
|
|
case "between":
|
|
|
|
|
if msg := checkSize(value, rulekey, ruleval, cmsgs); msg != "" {
|
|
|
|
|
errmsgs[rulekey] = msg
|
|
|
|
|
} else {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 15:42:42 +08:00
|
|
|
|
// 自定义正则判断
|
|
|
|
|
case "regex":
|
|
|
|
|
// 需要判断是否被|符号截断,如果是,那么需要进行整合
|
|
|
|
|
for i := index + 1; i < len(items); i++ {
|
|
|
|
|
// 判断下一个规则是否合法,不合法那么和当前正则规则进行整合
|
|
|
|
|
if !gregx.IsMatchString(gSINGLE_RULE_PATTERN, items[i]) {
|
|
|
|
|
ruleval += "|" + items[i]
|
|
|
|
|
index++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
match = gregx.IsMatchString(ruleval, value)
|
2017-12-29 00:03:32 +08:00
|
|
|
|
|
|
|
|
|
// 日期格式,
|
2017-12-28 17:28:32 +08:00
|
|
|
|
case "date":
|
2017-12-29 00:03:32 +08:00
|
|
|
|
for _, v := range []string{"2006-01-02", "20060102", "2006.01.02"} {
|
|
|
|
|
if _, err := gtime.StrToTime(value, v); err == nil {
|
|
|
|
|
match = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 日期格式,需要给定日期格式
|
2017-12-29 15:42:42 +08:00
|
|
|
|
case "date-format":
|
2017-12-28 17:28:32 +08:00
|
|
|
|
if _, err := gtime.StrToTime(value, ruleval); err == nil {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 00:03:32 +08:00
|
|
|
|
// 两字段值应相同(非敏感字符判断,非类型判断)
|
|
|
|
|
case "same":
|
|
|
|
|
if v, ok := params[ruleval]; ok {
|
|
|
|
|
if strings.Compare(value, v) == 0 {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 两字段值不应相同(非敏感字符判断,非类型判断)
|
|
|
|
|
case "different":
|
|
|
|
|
match = true
|
|
|
|
|
if v, ok := params[ruleval]; ok {
|
|
|
|
|
if strings.Compare(value, v) == 0 {
|
|
|
|
|
match = false
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-28 17:28:32 +08:00
|
|
|
|
|
|
|
|
|
// 字段值应当在指定范围中
|
|
|
|
|
case "in":
|
2017-12-29 15:42:42 +08:00
|
|
|
|
array := strings.Split(ruleval, ",")
|
2017-12-28 17:28:32 +08:00
|
|
|
|
for _, v := range array {
|
|
|
|
|
if strings.Compare(value, strings.TrimSpace(v)) == 0 {
|
|
|
|
|
match = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 字段值不应当在指定范围中
|
2017-12-29 15:42:42 +08:00
|
|
|
|
case "not-in":
|
2017-12-28 17:28:32 +08:00
|
|
|
|
match = true
|
2017-12-29 15:42:42 +08:00
|
|
|
|
array := strings.Split(ruleval, ",")
|
2017-12-28 17:28:32 +08:00
|
|
|
|
for _, v := range array {
|
|
|
|
|
if strings.Compare(value, strings.TrimSpace(v)) == 0 {
|
|
|
|
|
match = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 验证所给手机号码是否符合手机号的格式.
|
|
|
|
|
* 移动:134、135、136、137、138、139、150、151、152、157、158、159、182、183、184、187、188、178(4G)、147(上网卡);
|
|
|
|
|
* 联通:130、131、132、155、156、185、186、176(4G)、145(上网卡)、175;
|
|
|
|
|
* 电信:133、153、180、181、189 、177(4G);
|
|
|
|
|
* 卫星通信: 1349
|
|
|
|
|
* 虚拟运营商:170、173
|
|
|
|
|
*/
|
|
|
|
|
case "phone":
|
|
|
|
|
match = gregx.IsMatchString(`^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^17[0,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$`, value)
|
|
|
|
|
|
2017-12-29 00:03:32 +08:00
|
|
|
|
// 国内座机电话号码:"XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
|
2017-12-28 17:28:32 +08:00
|
|
|
|
case "telephone":
|
|
|
|
|
match = gregx.IsMatchString(`^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, value)
|
|
|
|
|
|
|
|
|
|
// 腾讯QQ号,从10000开始
|
|
|
|
|
case "qq":
|
|
|
|
|
match = gregx.IsMatchString(`^[1-9][0-9]{4,}$`, value)
|
|
|
|
|
|
|
|
|
|
// 中国邮政编码
|
|
|
|
|
case "postcode":
|
|
|
|
|
match = gregx.IsMatchString(`^[1-9]\d{5}$`, value)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
公民身份证号
|
|
|
|
|
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{2}$
|
|
|
|
|
|
|
|
|
|
总:
|
|
|
|
|
(^[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{2}$)
|
|
|
|
|
*/
|
2017-12-29 15:42:42 +08:00
|
|
|
|
case "id-number":
|
2017-12-28 17:28:32 +08:00
|
|
|
|
match = gregx.IsMatchString(`(^[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{2}$)`, value)
|
|
|
|
|
|
|
|
|
|
// 通用帐号规则(字母开头,只能包含字母、数字和下划线,长度在6~18之间)
|
|
|
|
|
case "passport":
|
|
|
|
|
match = gregx.IsMatchString(`^[a-zA-Z]{1}\w{5,17}$`, value)
|
|
|
|
|
|
|
|
|
|
// 通用密码(任意可见字符,长度在6~18之间)
|
|
|
|
|
case "password":
|
|
|
|
|
match = gregx.IsMatchString(`^[\w\S]{6,18}$`, value)
|
|
|
|
|
|
|
|
|
|
// 中等强度密码(在弱密码的基础上,必须包含大小写字母和数字)
|
|
|
|
|
case "password2":
|
|
|
|
|
if gregx.IsMatchString(`^[\w\S]{6,18}$`, value) && gregx.IsMatchString(`[a-z]+`, value) && gregx.IsMatchString(`[A-Z]+`, value) && gregx.IsMatchString(`\d+`, value) {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-29 16:56:49 +08:00
|
|
|
|
// 强等强度密码(在弱密码的基础上,必须包含大小写字母、数字和特殊字符)
|
2017-12-28 17:28:32 +08:00
|
|
|
|
case "password3":
|
|
|
|
|
if gregx.IsMatchString(`^[\w\S]{6,18}$`, value) && gregx.IsMatchString(`[a-z]+`, value) && gregx.IsMatchString(`[A-Z]+`, value) && gregx.IsMatchString(`\d+`, value) && gregx.IsMatchString(`\S+`, value) {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// json
|
|
|
|
|
case "json":
|
|
|
|
|
if _, err := gjson.Decode([]byte(value)); err == nil {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 整数
|
|
|
|
|
case "integer":
|
|
|
|
|
if _, err := strconv.Atoi(value); err == nil {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 小数
|
|
|
|
|
case "float":
|
|
|
|
|
if _, err := strconv.ParseFloat(value, 10); err == nil {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 布尔值(1,true,on,yes:true | 0,false,off,no,"":false)
|
|
|
|
|
case "boolean":
|
|
|
|
|
if value != "" && value != "0" && value != "false" && value != "off" && value != "no" {
|
|
|
|
|
match = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 邮件
|
|
|
|
|
case "email":
|
|
|
|
|
match = gregx.IsMatchString(`^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$`, value)
|
|
|
|
|
|
|
|
|
|
// URL
|
|
|
|
|
case "url":
|
|
|
|
|
match = gregx.IsMatchString(`^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$`, value)
|
|
|
|
|
|
2017-12-29 15:42:42 +08:00
|
|
|
|
// domain
|
|
|
|
|
case "domain":
|
|
|
|
|
match = gregx.IsMatchString(`^([0-9a-zA-Z][0-9a-zA-Z-]{0,62}\.)+([0-9a-zA-Z][0-9a-zA-Z-]{0,62})\.?$`, value)
|
|
|
|
|
|
|
|
|
|
// IP(IPv4/IPv6)
|
2017-12-28 17:28:32 +08:00
|
|
|
|
case "ip":
|
2017-12-29 15:42:42 +08:00
|
|
|
|
match = gipv4.Validate(value) || gipv6.Validate(value)
|
|
|
|
|
|
|
|
|
|
// IPv4
|
|
|
|
|
case "ipv4":
|
|
|
|
|
match = gipv4.Validate(value)
|
|
|
|
|
|
|
|
|
|
// IPv6
|
|
|
|
|
case "ipv6":
|
|
|
|
|
match = gipv6.Validate(value)
|
2017-12-28 17:28:32 +08:00
|
|
|
|
|
|
|
|
|
// MAC地址
|
|
|
|
|
case "mac":
|
|
|
|
|
match = gregx.IsMatchString(`^([0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2}$`, value)
|
|
|
|
|
|
|
|
|
|
default:
|
2017-12-29 15:42:42 +08:00
|
|
|
|
errmsgs[rulekey] = "Invalid rule name:" + rulekey
|
2017-12-28 17:28:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 错误消息整合
|
|
|
|
|
if !match {
|
2017-12-29 16:56:49 +08:00
|
|
|
|
// 不存在则使用默认的错误信息,
|
|
|
|
|
// 如果在校验过程中已经设置了错误信息,那么这里便不作处理
|
|
|
|
|
if _, ok := errmsgs[rulekey]; !ok {
|
|
|
|
|
if msg, ok := cmsgs[rulekey]; ok {
|
|
|
|
|
errmsgs[rulekey] = msg
|
|
|
|
|
} else {
|
2017-12-29 15:42:42 +08:00
|
|
|
|
errmsgs[rulekey] = errorMsgMap.Get(rulekey)
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-28 17:28:32 +08:00
|
|
|
|
}
|
2017-12-29 15:42:42 +08:00
|
|
|
|
index++
|
2017-12-28 17:28:32 +08:00
|
|
|
|
}
|
2017-12-29 15:42:42 +08:00
|
|
|
|
if len(errmsgs) > 0 {
|
|
|
|
|
return errmsgs
|
2017-12-28 17:28:32 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|