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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (f *Field) IsEmbedded() bool {
|
||||
return f.Field.Anonymous
|
||||
|
@ -464,3 +464,8 @@ func (t *Time) UnmarshalText(data []byte) error {
|
||||
}
|
||||
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
|
||||
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 (
|
||||
// regular expression pattern for single validation rule.
|
||||
singleRulePattern = `^([\w-]+):{0,1}(.*)`
|
||||
invalidRulesErrKey = "invalid_rules"
|
||||
invalidParamsErrKey = "invalid_params"
|
||||
invalidObjectErrKey = "invalid_object"
|
||||
|
||||
// no validation tag name for struct attribute.
|
||||
noValidationTagName = "nv"
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultValidator is the default validator for package functions.
|
||||
defaultValidator = New()
|
||||
defaultValidator = New() // defaultValidator is the default validator for package functions.
|
||||
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.
|
||||
internalErrKeyMap = map[string]string{
|
||||
|
@ -10,24 +10,9 @@ import (
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"reflect"
|
||||
"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.
|
||||
//
|
||||
// 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 {
|
||||
var (
|
||||
errorMaps = make(ErrorMap) // Returning error.
|
||||
// Returning error.
|
||||
errorMaps = make(ErrorMap)
|
||||
)
|
||||
fieldMap, err := structs.FieldMap(input.Object, aliasNameTagPriority, true)
|
||||
if err != nil {
|
||||
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 {
|
||||
if field.OriginalKind() == reflect.Struct {
|
||||
if err := v.CheckStruct(field.Value, input.CustomRules, input.CustomErrorMessageMap); err != nil {
|
||||
if field.IsEmbedded() {
|
||||
// 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.
|
||||
for k, m := range err.errors {
|
||||
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) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Pass struct {
|
||||
@ -261,13 +312,13 @@ func Test_CheckStruct_With_StructAttribute(t *testing.T) {
|
||||
Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"`
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Name string `valid:"name@required#请输入您的姓名"`
|
||||
Passwords Pass
|
||||
Pass
|
||||
Id int
|
||||
Name string `valid:"name@required#请输入您的姓名"`
|
||||
}
|
||||
user := &User{
|
||||
Name: "",
|
||||
Passwords: Pass{
|
||||
Pass: Pass{
|
||||
Pass1: "1",
|
||||
Pass2: "2",
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user