完成单数据校验规则开发

This commit is contained in:
John
2017-12-29 15:42:42 +08:00
parent 3f7bf3ce74
commit 905b5ea27a
9 changed files with 534 additions and 474 deletions

View File

@ -1,232 +0,0 @@
package gxml
import (
"fmt"
"strings"
"strconv"
"io/ioutil"
"encoding/xml"
)
// xml解析结果存放数组
type Xml struct {
// 注意这是一个指针
value *interface{}
}
// 一个xml变量
type XmlVar interface{}
// 编码go变量为xml字符串并返回xml字符串指针
func Encode (v interface{}) ([]byte, error) {
return xml.Marshal(v)
}
// 解码字符串为interface{}变量
func Decode (b []byte) (interface{}, error) {
var v interface{}
if err := DecodeTo(b, &v); err == nil {
return nil, err
} else {
return v, nil
}
}
// 解析xml字符串为go变量注意第二个参数为指针
func DecodeTo (b []byte, v interface{}) error {
return xml.Unmarshal(b, v)
}
// 解析xml字符串为gxml.Xml对象并返回操作对象指针
func DecodeToXml (b []byte) (*Xml, error) {
if v, err := Decode(b); err != nil {
return &Xml{&v}, nil
} else {
return nil, err
}
}
// 加载xml文件内容并转换为xml对象
func Load (path string) (*Xml, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var result interface{}
if err := xml.Unmarshal(data, &result); err != nil {
return nil, err
}
return &Xml{ &result }, nil
}
// 将变量转换为Xml对象进行处理该变量至少应当是一个map或者array否者转换没有意义
func NewXml(v *interface{}) *Xml {
return &Xml{ v }
}
// 将指定的xml内容转换为指定结构返回查找失败或者转换失败目标对象转换为nil
// 注意第二个参数需要给的是变量地址
func (p *Xml) GetToVar(pattern string, v interface{}) error {
r := p.Get(pattern)
if r != nil {
if t, err := Encode(r); err == nil {
return DecodeTo(t, v)
} else {
return err
}
} else {
v = nil
}
return nil
}
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与xml类型不匹配那么将会返回nil
func (p *Xml) GetMap(pattern string) map[string]interface{} {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(map[string]interface{}); ok {
return r
}
}
return nil
}
// 将检索值转换为Xml对象指针返回
func (p *Xml) GetXml(pattern string) *Xml {
result := p.Get(pattern)
if result != nil {
return &Xml{&result}
}
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与xml类型不匹配那么将会返回nil
func (p *Xml) GetArray(pattern string) []interface{} {
result := p.Get(pattern)
if result != nil {
if r, ok := result.([]interface{}); ok {
return r
}
}
return nil
}
// 返回指定xml中的string
func (p *Xml) GetString(pattern string) string {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(string); ok {
return r
}
}
return ""
}
// 返回指定xml中的bool
func (p *Xml) GetBool(pattern string) bool {
result := p.Get(pattern)
if result != nil {
str := fmt.Sprintf("%v", result)
if str != "" && str != "0" && str != "false" {
return true
}
}
return false
}
// 返回指定xml中的float64
func (p *Xml) GetFloat64(pattern string) float64 {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(float64); ok {
return r
}
}
return 0
}
// 返回指定xml中的float64->int
func (p *Xml) GetInt(pattern string) int {
return int(p.GetFloat64(pattern))
}
// 返回指定xml中的float64->int64
func (p *Xml) GetInt64(pattern string) int64 {
return int64(p.GetFloat64(pattern))
}
// 根据约定字符串方式访问xml解析数据参数形如 "items.name.first", "list.0"
// 返回的结果类型的interface{},因此需要自己做类型转换
// 如果找不到对应节点的数据返回nil
func (p *Xml) Get(pattern string) interface{} {
var result interface{}
pointer := p.value
array := strings.Split(pattern, ".")
length := len(array)
for i:= 0; i < length; i++ {
switch (*pointer).(type) {
case map[string]interface{}:
if v, ok := (*pointer).(map[string]interface{})[array[i]]; ok {
if i == length - 1 {
result = v
} else {
pointer = &v
}
} else {
return nil
}
case []interface{}:
if isNumeric(array[i]) {
n, err := strconv.Atoi(array[i])
if err == nil && len((*pointer).([]interface{})) > n {
if i == length - 1 {
result = (*pointer).([]interface{})[n]
break;
} else {
pointer = &(*pointer).([]interface{})[n]
}
}
} else {
return nil
}
default:
return nil
}
}
return result
}
// 转换为map[string]interface{}类型,如果转换失败返回nil
func (p *Xml) ToMap() map[string]interface{} {
pointer := p.value
switch (*pointer).(type) {
case map[string]interface{}:
return (*pointer).(map[string]interface{})
default:
return nil
}
}
// 转换为[]interface{}类型,如果转换失败返回nil
func (p *Xml) ToArray() []interface{} {
pointer := p.value
switch (*pointer).(type) {
case []interface{}:
return (*pointer).([]interface{})
default:
return nil
}
}
// 判断所给字符串是否为数字
func isNumeric(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] < byte('0') || s[i] > byte('9') {
return false
}
}
return true
}

View File

@ -1,32 +0,0 @@
package gxml_test
import (
"testing"
"fmt"
"encoding/xml"
)
var content = `<?xml version="1.0" encoding="utf-8"?>
<servers version="1">
<server>
<serverName>Shanghai_VPN</serverName>
<serverIP>127.0.0.1</serverIP>
</server>
<server>
<serverName>Beijing_VPN</serverName>
<serverIP>127.0.0.2</serverIP>
</server>
</servers>`
func Test_Xml(t *testing.T) {
//xml, err := gxml.Decode(bytes.TrimSpace([]byte(content)))
//if err != nil {
// glog.Error(err)
//}
//v := make(map[string]interface{})
v := make([]interface{}, 0)
e := xml.Unmarshal([]byte(content), &v)
fmt.Println(e)
fmt.Println(v)
}

View File

@ -6,8 +6,14 @@ import (
"strings"
"regexp"
"fmt"
"gitee.com/johng/gf/g/util/gregx"
)
// 判断所给地址是否是一个IPv4地址
func Validate(ip string) bool {
return gregx.IsMatchString(`^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$`, ip)
}
// ip字符串转为整形
func Ip2long(ipstr string) (ip uint32) {
reg, _ := regexp.Compile(`^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$`)

9
g/net/gipv6/gipv6.go Normal file
View File

@ -0,0 +1,9 @@
package gipv6
import "gitee.com/johng/gf/g/util/gregx"
// 判断所给地址是否是一个IPv6地址
func Validate(ip string) bool {
return gregx.IsMatchString(`^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}$|^:((:[\da-fA-F]{1,4}){1,6}|:)$|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)$|^([\da-fA-F]{1,4}:){2}((:[\da-fA-F]{1,4}){1,4}|:)$|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)$|^([\da-fA-F]{1,4}:){4}((:[\da-fA-F]{1,4}){1,2}|:)$|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?$|^([\da-fA-F]{1,4}:){6}:$`, ip)
}

View File

@ -2,7 +2,6 @@ package gtime
import (
"time"
"fmt"
)
// 类似与js中的SetTimeout一段时间后执行回调函数

View File

@ -17,6 +17,25 @@ func IsMatchString(pattern string, src string) bool {
return IsMatch(pattern, []byte(src))
}
// 正则匹配,并返回匹配的列表
func MatchString(pattern string, src string) ([]string, error) {
reg, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
s := reg.FindStringSubmatch(src)
return s, nil
}
func MatchAllString(pattern string, src string) ([][]string, error) {
reg, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
s := reg.FindAllStringSubmatch(src, -1)
return s, nil
}
// 正则替换(全部替换)
func Replace(pattern string, src, replace []byte) ([]byte, error) {
reg, err := regexp.Compile(pattern)

View File

@ -3,41 +3,46 @@
/*
参考https://laravel.com/docs/5.5/validation#available-validation-rules
规则如下:
required 格式required 说明:必需参数
required_if 格式required_if:field,value,... 说明:必需参数(当给定字段值与所给任意值相等时)
required_with 格式required_with:foo,bar,... 说明:必需参数(当所给定任意字段值不为空时)
required_with_all 格式required_with_all:foo,bar,... 说明:必参数(当所给定所有字段值不为空时)
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 格式:id_number 说明:中国邮政编码
id_number 格式:id_number 说明:公民身份证号码
qq 格式:qq 说明:腾讯QQ号码
ip 格式:ip 说明:IP地址(IPv4)
mac 格式:mac 说明:MAC地址
url 格式:url 说明:URL
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
xml 格式:xml 说明:判断数据格式为XML
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:foo,bar,... 说明参数值应该在foo,bar,...中
not_in 格式:not_in:foo,bar,... 说明参数值不应该在foo,bar,...中
regex 格式:regex:pattern 说明参数值应当满足正则匹配规则pattern(使用preg_match判断)
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
*/
package gvalid
@ -48,129 +53,237 @@ import (
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gregx"
"gitee.com/johng/gf/g/encoding/gjson"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/net/gipv6"
"gitee.com/johng/gf/g/net/gipv4"
)
// 默认规则校验错误消息(可以通过接口自定义错误消息)
var defaultMessages = map[string]string {
"required" : "字段不能为空",
"required_if" : "字段不能为空",
"required_with" : "字段不能为空",
"required_with_all" : "字段不能为空",
"date" : "日期格式不正确",
"email" : "邮箱地址格式不正确",
"phone" : "手机号码格式不正确",
"telephone" : "电话号码格式不正确",
"passport" : "账号格式不合法必需以字母开头只能包含字母、数字和下划线长度在6~18之间",
"password" : "密码格式不合法密码格式为任意6-18位的可见字符",
"password2" : "码格式不合法密码格式为任意6-18位的可见字符必须包含大小写字母和数字",
"password3" : "码格式不合法密码格式为任意6-18位的可见字符必须包含大小写字母、数字和特殊字符",
"postcode" : "邮政编码不正确",
"id_number" : "身份证号码不正确",
"qq" : "QQ号码格式不正确",
"ip" : "IP地址格式不正确",
"mac" : "MAC地址格式不正确",
"url" : "URL地址格式不正确",
"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" : "字段值不合法",
"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" : "字段值不合法",
}
// 检测一条数据的规则其中values参数为非必须参数可以传递所有的校验参数进来进行多参数对比(部分校验规则需要)
func CheckRule(value, rule string, values...map[string]string) map[string]string {
msgs := make(map[string]string)
params := make(map[string]string)
if len(values) > 0 {
params = values[0]
}
items := strings.Split(strings.TrimSpace(rule), "|")
for _, item := range items {
reg, _ := regexp.Compile(`^(\w+):{0,1}(.*)`)
results := reg.FindStringSubmatch(item)
rulekey := results[1]
ruleval := results[2]
match := false
switch rulekey {
// 必须字段
case "required":
match = !(value == "")
break
const (
gSINGLE_RULE_PATTERN = `^([\w-]+):{0,1}(.*)` // 单条规则匹配正则
)
// 必须字段(当给定字段值与所给任意值相等时)
case "required_if":
required := false
array := strings.Split(strings.TrimSpace(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 {
required = true
break
}
}
i += 2
}
}
if required {
match = !(value == "")
} else {
match = true
}
break
// 默认错误消息管理对象(并发安全)
var errorMsgMap = gmap.NewStringStringMap()
// 必须字段(当所给定任意字段值不为空时)
case "required_with":
required := false
array := strings.Split(strings.TrimSpace(ruleval), ",")
for i := 0; i < len(array); i++ {
if v, ok := params[array[i]]; ok {
if v != "" {
// 单规则正则对象,这里使用包内部变量存储,不需要多次解析
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 {
required = true
break
}
}
i += 2
}
if required {
match = !(value == "")
} else {
match = true
}
break
}
// 必须字段(当所给定所有字段值都不为空时)
case "required_with_all":
required := true
array := strings.Split(strings.TrimSpace(ruleval), ",")
for i := 0; i < len(array); i++ {
if v, ok := params[array[i]]; ok {
if v == "" {
// 必须字段(当所给定字段值与所给值都不相等时)
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 {
required = false
break
}
}
i += 2
}
if required {
match = !(value == "")
} else {
match = true
}
// 必须字段(当所给定任意字段值不为空时)
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
}
}
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
}
}
}
// 必须字段(当所给定所有字段值都为空时)
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
}
}
// 检测单条数据的规则其中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)
// 自定义正则判断
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)
// 日期格式,
case "date":
@ -180,14 +293,12 @@ func CheckRule(value, rule string, values...map[string]string) map[string]string
break
}
}
break
// 日期格式,需要给定日期格式
case "date_format":
case "date-format":
if _, err := gtime.StrToTime(value, ruleval); err == nil {
match = true
}
break
// 两字段值应相同(非敏感字符判断,非类型判断)
case "same":
@ -196,7 +307,6 @@ func CheckRule(value, rule string, values...map[string]string) map[string]string
match = true
}
}
break
// 两字段值不应相同(非敏感字符判断,非类型判断)
case "different":
@ -206,35 +316,27 @@ func CheckRule(value, rule string, values...map[string]string) map[string]string
match = false
}
}
break
// 字段值应当在指定范围中
case "in":
array := strings.Split(strings.TrimSpace(ruleval), ",")
array := strings.Split(ruleval, ",")
for _, v := range array {
if strings.Compare(value, strings.TrimSpace(v)) == 0 {
match = true
break
}
}
break
// 字段值不应当在指定范围中
case "not_in":
case "not-in":
match = true
array := strings.Split(strings.TrimSpace(ruleval), ",")
array := strings.Split(ruleval, ",")
for _, v := range array {
if strings.Compare(value, strings.TrimSpace(v)) == 0 {
match = false
break
}
}
break
// 自定义正则判断
//case "regex":
// $ruleMatch = @preg_match($ruleAttr, $value) ? true : false;
// break
/*
* 验证所给手机号码是否符合手机号的格式.
@ -246,22 +348,18 @@ func CheckRule(value, rule string, values...map[string]string) map[string]string
*/
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)
break
// 国内座机电话号码:"XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
case "telephone":
match = gregx.IsMatchString(`^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, value)
break
// 腾讯QQ号从10000开始
case "qq":
match = gregx.IsMatchString(`^[1-9][0-9]{4,}$`, value)
break
// 中国邮政编码
case "postcode":
match = gregx.IsMatchString(`^[1-9]\d{5}$`, value)
break
/*
公民身份证号
@ -284,182 +382,218 @@ func CheckRule(value, rule string, values...map[string]string) map[string]string
总:
(^[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}$)
*/
case "id_number":
case "id-number":
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)
break
// 通用帐号规则(字母开头只能包含字母、数字和下划线长度在6~18之间)
case "passport":
match = gregx.IsMatchString(`^[a-zA-Z]{1}\w{5,17}$`, value)
break
// 通用密码(任意可见字符长度在6~18之间)
case "password":
match = gregx.IsMatchString(`^[\w\S]{6,18}$`, value)
break
// 中等强度密码(在弱密码的基础上,必须包含大小写字母和数字)
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
}
break
// 强等强度密码(在弱密码的基础上,必须包含大小写字母、数字和特殊字符)
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
}
break
// 长度范围
case "length":
array := strings.Split(strings.TrimSpace(ruleval), ",")
array := strings.Split(ruleval, ",")
min := 0
max := 0
if len(array) > 0 {
if v, err := strconv.Atoi(array[0]); err == nil {
if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil {
min = v
}
}
if len(array) > 1 {
if v, err := strconv.Atoi(array[1]); err == nil {
if v, err := strconv.Atoi(strings.TrimSpace(array[1])); err == nil {
max = v
}
}
if len(value) >= min && len(value) <= max {
match = true
} else {
msg := errorMsgMap.Get(rulekey)
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
errmsgs[rulekey] = msg
}
break
// 最小长度
case "min_length":
if min, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil {
case "min-length":
if min, err := strconv.Atoi(ruleval); err == nil {
if len(value) >= min {
match = true
} else {
msg := errorMsgMap.Get(rulekey)
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
errmsgs[rulekey] = msg
}
} else {
errmsgs[rulekey] = "校验参数[" + ruleval + "]应当为整数类型"
}
break
// 最大长度
case "max_length":
if max, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil {
case "max-length":
if max, err := strconv.Atoi(ruleval); err == nil {
if len(value) <= max {
match = true
} else {
msg := errorMsgMap.Get(rulekey)
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
errmsgs[rulekey] = msg
}
} else {
errmsgs[rulekey] = "校验参数[" + ruleval + "]应当为整数类型"
}
break
// 大小范围
case "between":
array := strings.Split(strings.TrimSpace(ruleval), ",")
min := 0
max := 0
array := strings.Split(ruleval, ",")
min := float64(0)
max := float64(0)
if len(array) > 0 {
if v, err := strconv.Atoi(array[0]); err == nil {
if v, err := strconv.ParseFloat(strings.TrimSpace(array[0]), 10); err == nil {
min = v
}
}
if len(array) > 1 {
if v, err := strconv.Atoi(array[1]); err == nil {
if v, err := strconv.ParseFloat(strings.TrimSpace(array[1]), 10); err == nil {
max = v
}
}
if v, err := strconv.Atoi(value); err == nil {
if v, err := strconv.ParseFloat(value, 10); err == nil {
if v >= min && v <= max {
match = true
} else {
msg := errorMsgMap.Get(rulekey)
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
errmsgs[rulekey] = msg
}
} else {
errmsgs[rulekey] = "输入参数[" + value + "]应当为数字类型"
}
break
// 最小值
case "min":
if min, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil {
if v, err := strconv.Atoi(value); err == nil {
if min, err := strconv.ParseFloat(ruleval, 10); err == nil {
if v, err := strconv.ParseFloat(value, 10); err == nil {
if v >= min {
match = true
} else {
msg := errorMsgMap.Get(rulekey)
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
errmsgs[rulekey] = msg
}
} else {
errmsgs[rulekey] = "输入参数[" + value + "]应当为数字类型"
}
} else {
errmsgs[rulekey] = "校验参数[" + ruleval + "]应当为数字类型"
}
break
// 最大值
case "max":
if max, err := strconv.Atoi(strings.TrimSpace(ruleval)); err == nil {
if v, err := strconv.Atoi(value); err == nil {
if max, err := strconv.ParseFloat(ruleval, 10); err == nil {
if v, err := strconv.ParseFloat(value, 10); err == nil {
if v <= max {
match = true
} else {
msg := errorMsgMap.Get(rulekey)
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
errmsgs[rulekey] = msg
}
} else {
errmsgs[rulekey] = "输入参数[" + value + "]应当为数字类型"
}
} else {
errmsgs[rulekey] = "校验参数[" + ruleval + "]应当为数字类型"
}
break
// json
case "json":
if _, err := gjson.Decode([]byte(value)); err == nil {
match = true
}
break
//// xml
//case "xml":
// $checkResult = @Lib_XmlParser::isXml($value);
// $ruleMatch = ($checkResult !== null && $checkResult !== false);
// break
// 整数
case "integer":
if _, err := strconv.Atoi(value); err == nil {
match = true
}
break
// 小数
case "float":
if _, err := strconv.ParseFloat(value, 10); err == nil {
match = true
}
break
// 布尔值(1,true,on,yes:true | 0,false,off,no,"":false)
case "boolean":
if value != "" && value != "0" && value != "false" && value != "off" && value != "no" {
match = true
}
break
// 邮件
case "email":
match = gregx.IsMatchString(`^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$`, value)
break
// URL
case "url":
match = gregx.IsMatchString(`^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$`, value)
break
// IP
// 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)
case "ip":
match = gregx.IsMatchString(`^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$`, value)
break
match = gipv4.Validate(value) || gipv6.Validate(value)
// IPv4
case "ipv4":
match = gipv4.Validate(value)
// IPv6
case "ipv6":
match = gipv6.Validate(value)
// MAC地址
case "mac":
match = gregx.IsMatchString(`^([0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2}$`, value)
break
default:
msgs[rulekey] = "Invalid rule name:" + rulekey
break
errmsgs[rulekey] = "Invalid rule name:" + rulekey
}
// 错误消息整合
if !match {
msgs[rulekey] = defaultMessages[rulekey]
// 判断是否存在自定义的错误信息
if msg, ok := cmsgs[rulekey]; ok {
errmsgs[rulekey] = msg
} else {
// 不存在则使用默认的错误信息,
// 如果在校验过程中已经设置了错误信息,那么这里便不作处理
if _, ok := errmsgs[rulekey]; !ok {
errmsgs[rulekey] = errorMsgMap.Get(rulekey)
}
}
}
index++
}
if len(msgs) > 0 {
return msgs
if len(errmsgs) > 0 {
return errmsgs
}
return nil
}

View File

@ -0,0 +1,157 @@
package gvalid_test
import (
"testing"
"gitee.com/johng/gf/g/util/gvalid"
"strings"
)
func Test_Regex(t *testing.T) {
rule := `regex:\d{6}|\D{6}|length:6,16`
if m := gvalid.Check("123456", rule, nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("abcde6", rule, nil); m == nil {
t.Error("校验失败")
}
}
func Test_Required(t *testing.T) {
if m := gvalid.Check("1", "required", nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("", "required", nil); m == nil {
t.Error(m)
}
if m := gvalid.Check("", "required-if:id,1,age,18", nil, map[string]string{"id" : "1", "age" : "19"}); m == nil {
t.Error("required校验失败")
}
if m := gvalid.Check("", "required-if:id,1,age,18", nil, map[string]string{"id" : "2", "age" : "19"}); m != nil {
t.Error("required校验失败")
}
}
func Test_Ip(t *testing.T) {
if m := gvalid.Check("10.0.0.1", "ipv4", nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("0.0.0.0", "ipv4", nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("1920.0.0.0", "ipv4", nil); m == nil {
t.Error("ipv4校验失败")
}
if m := gvalid.Check("fe80::5484:7aff:fefe:9799", "ipv6", nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("fe80::5484:7aff:fefe:9799123", "ipv6", nil); m == nil {
t.Error(m)
}
}
func Test_Length(t *testing.T) {
rule := "length:6,16"
if m := gvalid.Check("123456", rule, nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("12345", rule, nil); m == nil {
t.Error("长度校验失败")
}
}
func Test_MinLength(t *testing.T) {
rule := "min-length:6"
if m := gvalid.Check("123456", rule, nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("12345", rule, nil); m == nil {
t.Error("长度校验失败")
}
}
func Test_MaxLength(t *testing.T) {
rule := "max-length:6"
if m := gvalid.Check("12345", rule, nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("1234567", rule, nil); m == nil {
t.Error("长度校验失败")
}
}
func Test_Between(t *testing.T) {
rule := "between:6.01, 10.01"
if m := gvalid.Check("10", rule, nil); m != nil {
t.Error(m)
}
if m := gvalid.Check("10.02", rule, nil); m == nil {
t.Error("大小范围校验失败")
}
}
func Test_SetDefaultErrorMsgs(t *testing.T) {
rule := "integer|length:6,16"
msgs := map[string]string {
"integer" : "请输入一个整数",
"length" : "参数长度不对啊老铁",
}
gvalid.SetDefaultErrorMsgs(msgs)
m := gvalid.Check("6.66", rule, nil)
if len(m) != 2 {
t.Error("规则校验失败")
} else {
if v, ok := m["integer"]; ok {
if strings.Compare(v, msgs["integer"]) != 0 {
t.Error("错误信息不匹配")
}
}
if v, ok := m["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 {
"integer" : "请输入一个整数",
"length" : "参数长度不对啊老铁",
}
m := gvalid.Check("6.66", rule, msgs)
if len(m) != 2 {
t.Error("规则校验失败")
} else {
if v, ok := m["integer"]; ok {
if strings.Compare(v, msgs["integer"]) != 0 {
t.Error("错误信息不匹配")
}
}
if v, ok := m["length"]; ok {
if strings.Compare(v, msgs["length"]) != 0 {
t.Error("错误信息不匹配")
}
}
}
}
func Test_CustomError2(t *testing.T) {
rule := "integer|length:6,16"
msgs := "请输入一个整数|参数长度不对啊老铁"
m := gvalid.Check("6.66", rule, msgs)
if len(m) != 2 {
t.Error("规则校验失败")
} else {
if v, ok := m["integer"]; ok {
if strings.Compare(v, "请输入一个整数") != 0 {
t.Error("错误信息不匹配")
}
}
if v, ok := m["length"]; ok {
if strings.Compare(v, "参数长度不对啊老铁") != 0 {
t.Error("错误信息不匹配")
}
}
}
}

View File

@ -2,10 +2,10 @@ package main
import (
"fmt"
"gitee.com/johng/gf/g/util/gregx"
"gitee.com/johng/gf/g/util/gvalid"
)
func main() {
fmt.Println(gregx.IsMatchString(`^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$`, "joh-n_cn@johng.cn"))
fmt.Println(gvalid.Check("10.0.0.0", "ip", nil))
}