diff --git a/net/ghttp/ghttp_request_param_param.go b/net/ghttp/ghttp_request_param_param.go index b5a625d71..1cb86c985 100644 --- a/net/ghttp/ghttp_request_param_param.go +++ b/net/ghttp/ghttp_request_param_param.go @@ -16,6 +16,16 @@ func (r *Request) SetParam(key string, value interface{}) { r.paramsMap[key] = value } +// SetParamMap sets custom parameter with key-value pair map. +func (r *Request) SetParamMap(data map[string]interface{}) { + if r.paramsMap == nil { + r.paramsMap = make(map[string]interface{}) + } + for k, v := range data { + r.paramsMap[k] = v + } +} + // GetParam returns custom parameter with given name `key`. // It returns `def` if `key` does not exist. // It returns nil if `def` is not passed. diff --git a/net/ghttp/ghttp_z_unit_feature_request_struct_test.go b/net/ghttp/ghttp_z_unit_feature_request_struct_test.go index b470addf3..45673c7f8 100644 --- a/net/ghttp/ghttp_z_unit_feature_request_struct_test.go +++ b/net/ghttp/ghttp_z_unit_feature_request_struct_test.go @@ -527,3 +527,50 @@ func Test_Params_Struct_Validation(t *testing.T) { t.Assert(c.PostContent(ctx, "/", `id=1`), `The name field is required`) }) } + +// https://github.com/gogf/gf/issues/1488 +func Test_Params_Parse_Issue1488(t *testing.T) { + p, _ := gtcp.GetFreePort() + s := g.Server(p) + s.Group("/", func(group *ghttp.RouterGroup) { + group.ALL("/", func(r *ghttp.Request) { + type Request struct { + Type []int `p:"type"` + Keyword string `p:"keyword"` + Limit int `p:"per_page" d:"10"` + Page int `p:"page" d:"1"` + Order string + CreatedAtLte string + CreatedAtGte string + CreatorID []int + } + for i := 0; i < 10; i++ { + r.SetParamMap(g.Map{ + "type[]": 0, + "keyword": "", + "t_start": "", + "t_end": "", + "reserve_at_start": "", + "reserve_at_end": "", + "user_name": "", + "flag": "", + "per_page": 6, + }) + var parsed Request + _ = r.Parse(&parsed) + r.Response.Write(parsed.Page, parsed.Limit) + } + }) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + c := g.Client() + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + t.Assert(c.GetContent(ctx, "/", ``), `16161616161616161616`) + }) +} diff --git a/os/gstructs/gstructs.go b/os/gstructs/gstructs.go index de1b40b46..b44cbccac 100644 --- a/os/gstructs/gstructs.go +++ b/os/gstructs/gstructs.go @@ -18,9 +18,15 @@ type Type struct { // Field contains information of a struct field . type Field struct { - Value reflect.Value // The underlying value of the field. - Field reflect.StructField // The underlying field of the field. - TagValue string // Retrieved tag value. There might be more than one tags in the field, but only one can be retrieved according to calling function rules. + Value reflect.Value // The underlying value of the field. + Field reflect.StructField // The underlying field of the field. + + // Retrieved tag name. It depends TagValue. + TagName string + + // Retrieved tag value. + // There might be more than one tags in the field, but only one can be retrieved according to calling function rules. + TagValue string } // FieldsInput is the input parameter struct type for function Fields. diff --git a/os/gstructs/gstructs_tag.go b/os/gstructs/gstructs_tag.go index d513fa9af..fdc563df8 100644 --- a/os/gstructs/gstructs_tag.go +++ b/os/gstructs/gstructs_tag.go @@ -180,7 +180,8 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap return nil, err } var ( - tagValue = "" + tagName string + tagValue string tagFields = make([]Field, 0) ) for _, field := range fields { @@ -190,6 +191,7 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap } tagValue = "" for _, p := range priority { + tagName = p tagValue = field.Tag(p) if tagValue != "" && tagValue != "-" { break @@ -201,6 +203,7 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap continue } tagField := field + tagField.TagName = tagName tagField.TagValue = tagValue tagFields = append(tagFields, tagField) } diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 47754a3df..c426ddf78 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -34,8 +34,8 @@ var ( } // StructTagPriority defines the default priority tags for Map*/Struct* functions. - // Note, the "gconv", "param", "params" tags are used by old version of package. - // It is strongly recommended using short tag "c" or "p" instead in the future. + // Note, the `gconv/param/params` tags are used by old version of package. + // It is strongly recommended using short tag `c/p` instead in the future. StructTagPriority = []string{"gconv", "param", "params", "c", "p", "json"} ) diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 03b3a5839..23a5c2595 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -248,6 +248,12 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // orm:"id, priority" // orm:"name, with:uid=id" tagMap[attributeName] = utils.RemoveSymbols(strings.Split(tagName, ",")[0]) + + // If tag and attribute values both exist in `paramsMap`, + // it then uses the tag value overwriting the attribute value in `paramsMap`. + if paramsMap[tagName] != nil && paramsMap[attributeName] != nil { + paramsMap[attributeName] = paramsMap[tagName] + } } var ( @@ -269,7 +275,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // string cases and chars like '-'/'_'/'.'/' '. // Matching the parameters to struct tag names. - // The `tagV` is the attribute name of the struct. + // The `attrKey` is the attribute name of the struct. for attrKey, cmpKey := range tagMap { if strings.EqualFold(checkName, cmpKey) { attrName = attrKey @@ -297,12 +303,12 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string continue } // If the attribute name is already checked converting, then skip it. - if _, ok := doneMap[attrName]; ok { + if _, ok = doneMap[attrName]; ok { continue } // Mark it done. doneMap[attrName] = struct{}{} - if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping); err != nil { + if err = bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping); err != nil { return err } }