mirror of
https://gitee.com/johng/gf.git
synced 2024-11-30 03:07:45 +08:00
fx issue #1250
This commit is contained in:
parent
302e234bfe
commit
2274a10cfd
@ -14,6 +14,16 @@ func (f *Field) Tag(key string) string {
|
|||||||
return f.Field.Tag.Get(key)
|
return f.Field.Tag.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TagLookup returns the value associated with key in the tag string.
|
||||||
|
// If the key is present in the tag the value (which may be empty)
|
||||||
|
// is returned. Otherwise the returned value will be the empty string.
|
||||||
|
// The ok return value reports whether the value was explicitly set in
|
||||||
|
// the tag string. If the tag does not have the conventional format,
|
||||||
|
// the value returned by Lookup is unspecified.
|
||||||
|
func (f *Field) TagLookup(key string) (value string, ok bool) {
|
||||||
|
return f.Field.Tag.Lookup(key)
|
||||||
|
}
|
||||||
|
|
||||||
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||||
func (f *Field) IsEmbedded() bool {
|
func (f *Field) IsEmbedded() bool {
|
||||||
return f.Field.Anonymous
|
return f.Field.Anonymous
|
||||||
|
@ -464,3 +464,8 @@ func (t *Time) UnmarshalText(data []byte) error {
|
|||||||
}
|
}
|
||||||
return gerror.Newf(`invalid time value: %s`, data)
|
return gerror.Newf(`invalid time value: %s`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoValidation marks this struct object will not be validated by package gvalid.
|
||||||
|
func (t *Time) NoValidation() {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -64,17 +64,35 @@ import (
|
|||||||
// like: map[field] => string|map[rule]string
|
// like: map[field] => string|map[rule]string
|
||||||
type CustomMsg = map[string]interface{}
|
type CustomMsg = map[string]interface{}
|
||||||
|
|
||||||
|
// doCheckStructWithParamMapInput is used for struct validation for internal function.
|
||||||
|
type doCheckStructWithParamMapInput struct {
|
||||||
|
Object interface{} // Can be type of struct/*struct.
|
||||||
|
ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `UseParamMapInsteadOfObjectValue`.
|
||||||
|
UseParamMapInsteadOfObjectValue bool // Using `ParamMap` as its validation source instead of values from `Object`.
|
||||||
|
CustomRules interface{} // Custom validation rules.
|
||||||
|
CustomErrorMessageMap CustomMsg // Custom error message map for validation rules.
|
||||||
|
}
|
||||||
|
|
||||||
|
// apiNoValidation is an interface that marks current struct not validated by package `gvalid`.
|
||||||
|
type apiNoValidation interface {
|
||||||
|
NoValidation()
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// regular expression pattern for single validation rule.
|
// regular expression pattern for single validation rule.
|
||||||
singleRulePattern = `^([\w-]+):{0,1}(.*)`
|
singleRulePattern = `^([\w-]+):{0,1}(.*)`
|
||||||
invalidRulesErrKey = "invalid_rules"
|
invalidRulesErrKey = "invalid_rules"
|
||||||
invalidParamsErrKey = "invalid_params"
|
invalidParamsErrKey = "invalid_params"
|
||||||
invalidObjectErrKey = "invalid_object"
|
invalidObjectErrKey = "invalid_object"
|
||||||
|
|
||||||
|
// no validation tag name for struct attribute.
|
||||||
|
noValidationTagName = "nv"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// defaultValidator is the default validator for package functions.
|
defaultValidator = New() // defaultValidator is the default validator for package functions.
|
||||||
defaultValidator = New()
|
structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array.
|
||||||
|
aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array.
|
||||||
|
|
||||||
// all internal error keys.
|
// all internal error keys.
|
||||||
internalErrKeyMap = map[string]string{
|
internalErrKeyMap = map[string]string{
|
||||||
|
@ -10,24 +10,9 @@ import (
|
|||||||
"github.com/gogf/gf/internal/structs"
|
"github.com/gogf/gf/internal/structs"
|
||||||
"github.com/gogf/gf/util/gconv"
|
"github.com/gogf/gf/util/gconv"
|
||||||
"github.com/gogf/gf/util/gutil"
|
"github.com/gogf/gf/util/gutil"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// doCheckStructWithParamMapInput is used for struct validation for internal function.
|
|
||||||
type doCheckStructWithParamMapInput struct {
|
|
||||||
Object interface{} // Can be type of struct/*struct.
|
|
||||||
ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `UseParamMapInsteadOfObjectValue`.
|
|
||||||
UseParamMapInsteadOfObjectValue bool // Using `ParamMap` as its validation source instead of values from `Object`.
|
|
||||||
CustomRules interface{} // Custom validation rules.
|
|
||||||
CustomErrorMessageMap CustomMsg // Custom error message map for validation rules.
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array.
|
|
||||||
aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array.
|
|
||||||
)
|
|
||||||
|
|
||||||
// CheckStruct validates struct and returns the error result.
|
// CheckStruct validates struct and returns the error result.
|
||||||
//
|
//
|
||||||
// The parameter `object` should be type of struct/*struct.
|
// The parameter `object` should be type of struct/*struct.
|
||||||
@ -70,16 +55,27 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa
|
|||||||
|
|
||||||
func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapInput) *Error {
|
func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapInput) *Error {
|
||||||
var (
|
var (
|
||||||
errorMaps = make(ErrorMap) // Returning error.
|
// Returning error.
|
||||||
|
errorMaps = make(ErrorMap)
|
||||||
)
|
)
|
||||||
fieldMap, err := structs.FieldMap(input.Object, aliasNameTagPriority, true)
|
fieldMap, err := structs.FieldMap(input.Object, aliasNameTagPriority, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newErrorStr("invalid_object", err.Error())
|
return newErrorStr("invalid_object", err.Error())
|
||||||
}
|
}
|
||||||
// It checks the struct recursively the its attribute is also a struct.
|
// It checks the struct recursively the its attribute is an embedded struct.
|
||||||
for _, field := range fieldMap {
|
for _, field := range fieldMap {
|
||||||
if field.OriginalKind() == reflect.Struct {
|
if field.IsEmbedded() {
|
||||||
if err := v.CheckStruct(field.Value, input.CustomRules, input.CustomErrorMessageMap); err != nil {
|
// No validation interface implements check.
|
||||||
|
if _, ok := field.Value.Interface().(apiNoValidation); ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := field.TagLookup(noValidationTagName); ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
recursiveInput := doCheckStructWithParamMapInput{}
|
||||||
|
recursiveInput = *input
|
||||||
|
recursiveInput.Object = field.Value
|
||||||
|
if err := v.doCheckStructWithParamMap(&recursiveInput); err != nil {
|
||||||
// It merges the errors into single error map.
|
// It merges the errors into single error map.
|
||||||
for k, m := range err.errors {
|
for k, m := range err.errors {
|
||||||
errorMaps[k] = m
|
errorMaps[k] = m
|
||||||
|
@ -228,6 +228,57 @@ func Test_CheckStruct(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_CheckStruct_EmbeddedObject_Attribute(t *testing.T) {
|
||||||
|
gtest.C(t, func(t *gtest.T) {
|
||||||
|
type Base struct {
|
||||||
|
Time *gtime.Time
|
||||||
|
}
|
||||||
|
type Object struct {
|
||||||
|
Base
|
||||||
|
Name string
|
||||||
|
Type int
|
||||||
|
}
|
||||||
|
rules := map[string]string{
|
||||||
|
"Name": "required",
|
||||||
|
"Type": "required",
|
||||||
|
}
|
||||||
|
ruleMsg := map[string]interface{}{
|
||||||
|
"Name": "名称必填",
|
||||||
|
"Type": "类型必填",
|
||||||
|
}
|
||||||
|
obj := &Object{}
|
||||||
|
obj.Type = 1
|
||||||
|
obj.Name = "john"
|
||||||
|
obj.Time = gtime.Now()
|
||||||
|
err := gvalid.CheckStruct(context.TODO(), obj, rules, ruleMsg)
|
||||||
|
t.Assert(err, nil)
|
||||||
|
})
|
||||||
|
gtest.C(t, func(t *gtest.T) {
|
||||||
|
type Base struct {
|
||||||
|
Name string
|
||||||
|
Type int
|
||||||
|
}
|
||||||
|
type Object struct {
|
||||||
|
Base Base
|
||||||
|
Name string
|
||||||
|
Type int
|
||||||
|
}
|
||||||
|
rules := map[string]string{
|
||||||
|
"Name": "required",
|
||||||
|
"Type": "required",
|
||||||
|
}
|
||||||
|
ruleMsg := map[string]interface{}{
|
||||||
|
"Name": "名称必填",
|
||||||
|
"Type": "类型必填",
|
||||||
|
}
|
||||||
|
obj := &Object{}
|
||||||
|
obj.Type = 1
|
||||||
|
obj.Name = "john"
|
||||||
|
err := gvalid.CheckStruct(context.TODO(), obj, rules, ruleMsg)
|
||||||
|
t.Assert(err, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func Test_CheckStruct_With_EmbeddedObject(t *testing.T) {
|
func Test_CheckStruct_With_EmbeddedObject(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
type Pass struct {
|
type Pass struct {
|
||||||
@ -261,13 +312,13 @@ func Test_CheckStruct_With_StructAttribute(t *testing.T) {
|
|||||||
Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"`
|
Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"`
|
||||||
}
|
}
|
||||||
type User struct {
|
type User struct {
|
||||||
Id int
|
Pass
|
||||||
Name string `valid:"name@required#请输入您的姓名"`
|
Id int
|
||||||
Passwords Pass
|
Name string `valid:"name@required#请输入您的姓名"`
|
||||||
}
|
}
|
||||||
user := &User{
|
user := &User{
|
||||||
Name: "",
|
Name: "",
|
||||||
Passwords: Pass{
|
Pass: Pass{
|
||||||
Pass1: "1",
|
Pass1: "1",
|
||||||
Pass2: "2",
|
Pass2: "2",
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user