diff --git a/net/ghttp/ghttp_unit_request_test.go b/net/ghttp/ghttp_unit_request_test.go index f7b28618a..26880df6b 100644 --- a/net/ghttp/ghttp_unit_request_test.go +++ b/net/ghttp/ghttp_unit_request_test.go @@ -586,3 +586,38 @@ func Test_Params_Parse_DefaultValueTag(t *testing.T) { t.Assert(client.PostContent("/parse", `{"name":"smith", "score":100}`), `{"Name":"smith","Score":100}`) }) } + +func Test_Params_Parse_Validation(t *testing.T) { + type RegisterReq struct { + Name string `p:"username" v:"required|length:6,30#请输入账号|账号长度为:min到:max位"` + Pass string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"` + Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|密码长度不够|两次密码不一致"` + } + + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/parse", func(r *ghttp.Request) { + var req *RegisterReq + if err := r.Parse(&req); err != nil { + r.Response.Write(err) + } else { + r.Response.Write("ok") + } + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + prefix := fmt.Sprintf("http://127.0.0.1:%d", p) + client := g.Client() + client.SetPrefix(prefix) + + t.Assert(client.GetContent("/parse"), `请输入账号; 账号长度为6到30位; 请输入密码; 密码长度不够; 请确认密码; 密码长度不够; 两次密码不一致`) + t.Assert(client.GetContent("/parse?name=john11&password1=123456&password2=123"), `密码长度不够; 两次密码不一致`) + t.Assert(client.GetContent("/parse?name=john&password1=123456&password2=123456"), `账号长度为6到30位`) + t.Assert(client.GetContent("/parse?name=john11&password1=123456&password2=123456"), `ok`) + }) +} diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 2c921568b..58426abfb 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -21,8 +21,8 @@ func (v *Validator) CheckStruct(object interface{}) Error { func (v *Validator) doCheckStruct(object interface{}) Error { var ( - // Returning error. - errorMaps = make(map[string]map[string]string) + errorMaps = make(map[string]map[string]string) // Returning error. + fieldToAliasNameMap = make(map[string]string) // Field name to alias name map. ) fieldMap, err := structs.FieldMap(object, aliasNameTagPriority, true) if err != nil { @@ -44,6 +44,10 @@ func (v *Validator) doCheckStruct(object interface{}) Error { errorMaps[k] = m } } + } else { + if field.TagValue != "" { + fieldToAliasNameMap[field.Name()] = field.TagValue + } } } // It here must use structs.TagFields not structs.FieldMap to ensure error sequence. @@ -61,8 +65,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { checkRules = make(map[string]string) customMessage = make(CustomMsg) checkValueData = v.data - fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. - errorRules = make([]string, 0) // Sequence rules. + errorRules = make([]string, 0) // Sequence rules. ) if checkValueData == nil { checkValueData = object @@ -128,19 +131,33 @@ func (v *Validator) doCheckStruct(object interface{}) Error { // Merge the custom validation rules with rules in struct tag. // The custom rules has the most high priority that can overwrite the struct tag rules. for _, field := range tagField { - fieldName := field.Name() - // sequence tag == struct tag - // The name here is alias of field name. - name, rule, msg := parseSequenceTag(field.TagValue) + var ( + fieldName = field.Name() // Attribute name. + name, rule, msg = parseSequenceTag(field.TagValue) // The `name` is different from `attribute alias`, which is used for validation only. + ) if len(name) == 0 { - name = fieldName + if v, ok := fieldToAliasNameMap[fieldName]; ok { + // It uses alias name of the attribute if its alias name tag exists. + name = v + } else { + // It or else uses the attribute name directly. + name = fieldName + } } else { - fieldAliases[fieldName] = name + // It uses the alias name from validation rule. + fieldToAliasNameMap[fieldName] = name } // It here extends the params map using alias names. + // Note that the variable `name` might be alias name or attribute name. if _, ok := inputParamMap[name]; !ok { if !v.useDataInsteadOfObjectAttributes { inputParamMap[name] = field.Value.Interface() + } else { + if name != fieldName { + if foundKey, foundValue := gutil.MapPossibleItemByKey(inputParamMap, fieldName); foundKey != "" { + inputParamMap[name] = foundValue + } + } } } if _, ok := checkRules[name]; !ok { @@ -184,7 +201,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { // which have the most priority than `rules` and struct tag. if msg, ok := v.messages.(CustomMsg); ok && len(msg) > 0 { for k, v := range msg { - if a, ok := fieldAliases[k]; ok { + if a, ok := fieldToAliasNameMap[k]; ok { // Overwrite the key of field name. customMessage[a] = v } else {