diff --git a/g/util/gvalid/gvalid_check.go b/g/util/gvalid/gvalid_check.go index 5e3460c9d..654108449 100644 --- a/g/util/gvalid/gvalid_check.go +++ b/g/util/gvalid/gvalid_check.go @@ -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) diff --git a/g/util/gvalid/gvalid_check_struct.go b/g/util/gvalid/gvalid_check_struct.go index fba014f95..bafdc09c0 100644 --- a/g/util/gvalid/gvalid_check_struct.go +++ b/g/util/gvalid/gvalid_check_struct.go @@ -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() diff --git a/g/util/gvalid/gvalid_unit_basic_array_slice_test.go b/g/util/gvalid/gvalid_unit_basic_array_slice_test.go new file mode 100644 index 000000000..83dff7702 --- /dev/null +++ b/g/util/gvalid/gvalid_unit_basic_array_slice_test.go @@ -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") + } + + }) +} diff --git a/g/util/gvalid/gvalid_unit_basic_map_test.go b/g/util/gvalid/gvalid_unit_basic_map_test.go new file mode 100644 index 000000000..6d792aa92 --- /dev/null +++ b/g/util/gvalid/gvalid_unit_basic_map_test.go @@ -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") + } + }) +}