Fixed use cname in gvalid tag.

Added gvaild data type support for array, slice, map.
Added array, slice, map data test in gvaild unit test.
This commit is contained in:
hailaz
2019-07-11 15:41:10 +08:00
parent df8623c4e2
commit cf745422f3
4 changed files with 184 additions and 8 deletions

View File

@ -7,16 +7,19 @@
package gvalid
import (
"reflect"
"regexp"
"strconv"
"strings"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/net/gipv4"
"github.com/gogf/gf/g/net/gipv6"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"regexp"
"strconv"
"strings"
)
const (
@ -104,7 +107,56 @@ var (
}
)
// 检测单条数据的规则:
// 检测单条数据
// value数据类型可以为map、array、slice以及其它基本数据类型
func Check(value interface{}, rules string, msgs interface{}, params ...interface{}) *Error {
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Array, reflect.Slice: // Array/Slice 遍历调用
for i := 0; i < rv.Len(); i++ {
err := Check(rv.Index(i).Interface(), rules, msgs, params...)
if err != nil {
return err
}
}
return nil
case reflect.Map: // Map 遍历调用
iter := rv.MapRange()
for iter.Next() {
err := Check(iter.Value().Interface(), rules, msgs, params...)
if err != nil {
return err
}
}
return nil
case reflect.Ptr: // Ptr
rv := rv.Elem()
array := make([]interface{}, 0)
switch rv.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < rv.Len(); i++ {
array = append(array, rv.Index(i).Interface())
}
return Check(array, rules, msgs, params...)
case reflect.Struct:
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
// Only public attributes.
if !gstr.IsLetterUpper(rt.Field(i).Name[0]) {
continue
}
array = append(array, rv.Field(i).Interface())
}
return Check(array, rules, msgs, params...)
}
return nil
}
return doCheckSingleRule(value, rules, msgs, params...)
}
// 基本数据类型校验
//
// 1. value为需要校验的数据可以为任意基本数据类型
//
@ -112,7 +164,7 @@ var (
// 允许传递多个自定义的错误信息如果类型为string那么中间使用"|"符号分隔多个自定义错误;
//
// 3. params参数为联合校验参数支持任意的map/struct/*struct类型对于需要联合校验的规则有效required-*、same、different
func Check(value interface{}, rules string, msgs interface{}, params ...interface{}) *Error {
func doCheckSingleRule(value interface{}, rules string, msgs interface{}, params ...interface{}) *Error {
// 内部会将参数全部转换为字符串类型进行校验
val := strings.TrimSpace(gconv.String(value))
data := make(map[string]string)

View File

@ -10,7 +10,6 @@ import (
"strings"
"github.com/gogf/gf/g/internal/structs"
"github.com/gogf/gf/g/util/gconv"
)
@ -22,6 +21,7 @@ var (
// 校验struct对象属性object参数也可以是一个指向对象的指针返回值同CheckMap方法。
// struct的数据校验结果信息是顺序的。
func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Error {
cname := make(map[string]string) // 别名记录
params := make(map[string]interface{})
checkRules := make(map[string]string)
customMsgs := make(CustomMsg)
@ -82,6 +82,8 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
name, rule, msg := parseSequenceTag(tagValue)
if len(name) == 0 {
name = fieldName
} else {
cname[fieldName] = name
}
// params参数使用别名**扩容**(而不仅仅使用别名),仅用于验证使用
if _, ok := params[name]; !ok {
@ -89,7 +91,12 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
}
// 校验规则
if _, ok := checkRules[name]; !ok {
checkRules[name] = rule
if _, ok := checkRules[fieldName]; ok {
checkRules[name] = checkRules[fieldName]
delete(checkRules, fieldName)
} else {
checkRules[name] = rule
}
errorRules = append(errorRules, name+"@"+rule)
} else {
// 传递的rules规则会覆盖struct tag的规则
@ -116,11 +123,16 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
}
}
}
// 自定义错误消息非必须参数优先级比rules参数中以及struct tag中定义的错误消息更高
if len(msgs) > 0 && len(msgs[0]) > 0 {
if len(customMsgs) > 0 {
for k, v := range msgs[0] {
customMsgs[k] = v
if cmane, ok := cname[k]; ok {
customMsgs[cmane] = v
} else {
customMsgs[k] = v
}
}
} else {
customMsgs = msgs[0]
@ -136,6 +148,8 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
value = nil
if v, ok := params[key]; ok {
value = v
} else { // 不存在的字段的规则跳过。例如rules使用[]string格式输入时没有对应字段便会出现这种情况。
continue
}
if e := Check(value, rule, customMsgs[key], params); e != nil {
_, item := e.FirstItem()

View File

@ -0,0 +1,64 @@
// Copyright 2017 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
package gvalid_test
import (
"testing"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gvalid"
)
func Test_Array(t *testing.T) {
gtest.Case(t, func() {
arrayData := [2]int{7, 8}
msgs := map[string]string{
"between": "not between",
"in": "not in",
}
err := gvalid.Check(arrayData, "between:6,10|in:7,8", msgs)
gtest.Assert(err, nil)
if e := gvalid.Check(arrayData, "between:9,10|in:7,8", msgs); e != nil {
gtest.Assert(e.String(), "not between")
}
if e := gvalid.Check(arrayData, "between:6,10|in:7", msgs); e != nil {
gtest.Assert(e.String(), "not in")
}
err1 := gvalid.Check(&arrayData, "between:6,10|in:7,8", msgs)
gtest.Assert(err1, nil)
if e := gvalid.Check(&arrayData, "between:9,10|in:7,8", msgs); e != nil {
gtest.Assert(e.String(), "not between")
}
if e := gvalid.Check(&arrayData, "between:6,10|in:7", msgs); e != nil {
gtest.Assert(e.String(), "not in")
}
})
}
func Test_Slice(t *testing.T) {
gtest.Case(t, func() {
sliceData := [][]string{[]string{"12345678", "12345678"}, []string{"12345678", "12345678"}}
msgs := map[string]string{
"length": "length err",
}
err := gvalid.Check(sliceData, "length:3,16", msgs)
gtest.Assert(err, nil)
if e := gvalid.Check(sliceData, "length:9,16", msgs); e != nil {
gtest.Assert(e.String(), "length err")
}
})
}

View File

@ -0,0 +1,46 @@
// Copyright 2017 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
package gvalid_test
import (
"testing"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gvalid"
)
func Test_Map(t *testing.T) {
type Test struct {
Id int
}
gtest.Case(t, func() {
mapData := map[string]interface{}{
"123": map[string]int{
"aaa": 6,
"bbb": 7,
"ccc": 8,
},
"456": &Test{
Id: 9,
},
}
err := gvalid.Check(mapData, "between:6,10|in:6,7,8,9", nil)
gtest.Assert(err, nil)
msgs := map[string]string{
"between": "not between",
"in": "not in",
}
if e := gvalid.Check(mapData, "between:10,10|in:6,7,8,9", msgs); e != nil {
gtest.Assert(e.String(), "not between")
}
if e := gvalid.Check(mapData, "between:6,10|in:10", msgs); e != nil {
gtest.Assert(e.String(), "not in")
}
})
}