From effe03e078ac02df3b6736fadb5a096993e4e533 Mon Sep 17 00:00:00 2001 From: Neenad Ingole Date: Sun, 13 Mar 2022 21:52:44 +0100 Subject: [PATCH] :adhesive_bandage: Allow parsing of square bracket query param --- ctx.go | 28 ++++++++++++++++++++++++++++ ctx_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/ctx.go b/ctx.go index 126c7ddd8bf..e033776b2e0 100644 --- a/ctx.go +++ b/ctx.go @@ -911,6 +911,8 @@ func (c *Ctx) QueryParser(out interface{}) error { k := utils.UnsafeString(key) v := utils.UnsafeString(val) + k = parseQuery(k) + if strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k) { values := strings.Split(v, ",") for i := 0; i < len(values); i++ { @@ -925,6 +927,32 @@ func (c *Ctx) QueryParser(out interface{}) error { return c.parseToStruct(queryTag, out, data) } +func parseQuery(k string) string { + // split all the character + chars := strings.Split(k, "") + // buffer to hold the converted chars + keyBuf := make([]string, 0) + + for _, char := range chars { + switch char { + case "[": + // convert [ to . as the gorilla/schema works with . notation + keyBuf = append(keyBuf, ".") + case "]": + // for key of format `data[]=john` the end would have `.` which will be dropped + if keyBuf[len(keyBuf)-1] == "." { + keyBuf = keyBuf[:len(keyBuf)-1] + } + default: + // append all the other chars to the buffer + keyBuf = append(keyBuf, char) + } + } + + // convert to string + return strings.Join(keyBuf, "") +} + // ReqHeaderParser binds the request header strings to a struct. func (c *Ctx) ReqHeaderParser(out interface{}) error { data := make(map[string][]string) diff --git a/ctx_test.go b/ctx_test.go index ee5520b33e9..927314369fd 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2931,6 +2931,14 @@ func Test_Ctx_QueryParser(t *testing.T) { rq := new(RequiredQuery) c.Request().URI().SetQueryString("") utils.AssertEqual(t, "name is empty", c.QueryParser(rq).Error()) + + type ArrayQuery struct { + Data []string + } + aq := new(ArrayQuery) + c.Request().URI().SetQueryString("data[]=john&data[]=doe") + utils.AssertEqual(t, nil, c.QueryParser(aq)) + utils.AssertEqual(t, 2, len(aq.Data)) } // go test -run Test_Ctx_QueryParser_WithSetParserDecoder -v @@ -3059,6 +3067,40 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { utils.AssertEqual(t, nil, c.QueryParser(n)) utils.AssertEqual(t, 3, n.Value) utils.AssertEqual(t, 0, n.Next.Value) + + type Person struct { + Name string `query:"name"` + Age int `query:"age"` + } + + type CollectionQuery struct { + Data []Person `query:"data"` + } + + c.Request().URI().SetQueryString("data[0][name]=john&data[0][age]=10&data[1][name]=doe&data[1][age]=12") + cq := new(CollectionQuery) + utils.AssertEqual(t, nil, c.QueryParser(cq)) + utils.AssertEqual(t, 2, len(cq.Data)) + utils.AssertEqual(t, "john", cq.Data[0].Name) + utils.AssertEqual(t, 10, cq.Data[0].Age) + utils.AssertEqual(t, "doe", cq.Data[1].Name) + utils.AssertEqual(t, 12, cq.Data[1].Age) + + c.Request().URI().SetQueryString("data.0.name=john&data.0.age=10&data.1.name=doe&data.1.age=12") + cq = new(CollectionQuery) + utils.AssertEqual(t, nil, c.QueryParser(cq)) + utils.AssertEqual(t, 2, len(cq.Data)) + utils.AssertEqual(t, "john", cq.Data[0].Name) + utils.AssertEqual(t, 10, cq.Data[0].Age) + utils.AssertEqual(t, "doe", cq.Data[1].Name) + utils.AssertEqual(t, 12, cq.Data[1].Age) + + c.Request().URI().SetQueryString("data[0]name]=john&data[0][age]=10&data[1][name]=doe&data[1][age]=12") + cq = new(CollectionQuery) + utils.AssertEqual(t, nil, c.QueryParser(cq)) + utils.AssertEqual(t, 2, len(cq.Data)) + utils.AssertEqual(t, "", cq.Data[0].Name) + utils.AssertEqual(t, 10, cq.Data[0].Age) } // go test -run Test_Ctx_ReqHeaderParser -v