Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support map as query string or post form parameters #1383

Merged
merged 8 commits into from Aug 6, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Expand Up @@ -26,6 +26,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
- [Querystring parameters](#querystring-parameters)
- [Multipart/Urlencoded Form](#multiparturlencoded-form)
- [Another example: query + post form](#another-example-query--post-form)
- [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters)
- [Upload files](#upload-files)
- [Grouping routes](#grouping-routes)
- [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default)
Expand Down Expand Up @@ -315,6 +316,34 @@ func main() {
id: 1234; page: 1; name: manu; message: this_is_great
```

### Map as querystring or postform parameters

```
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded

names[first]=thinkerou&names[second]=tianou
```

```go
func main() {
router := gin.Default()

router.POST("/post", func(c *gin.Context) {

ids := c.QueryMap("ids")
names := c.PostFormMap("names")

fmt.Printf("ids: %v; names: %v", ids, names)
})
router.Run(":8080")
}
```

```
ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]
```

### Upload files

#### Single file
Expand Down
48 changes: 48 additions & 0 deletions context.go
Expand Up @@ -361,6 +361,18 @@ func (c *Context) GetQueryArray(key string) ([]string, bool) {
return []string{}, false
}

// QueryMap returns a map for a given query key.
func (c *Context) QueryMap(key string) map[string]string {
dicts, _ := c.GetQueryMap(key)
return dicts
}

// GetQueryMap returns a map for a given query key, plus a boolean value
// whether at least one value exists for the given key.
func (c *Context) GetQueryMap(key string) (map[string]string, bool) {
return c.get(c.Request.URL.Query(), key)
}

// PostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns an empty string `("")`.
func (c *Context) PostForm(key string) string {
Expand Down Expand Up @@ -416,6 +428,42 @@ func (c *Context) GetPostFormArray(key string) ([]string, bool) {
return []string{}, false
}

// PostFormMap returns a map for a given form key.
func (c *Context) PostFormMap(key string) map[string]string {
dicts, _ := c.GetPostFormMap(key)
return dicts
}

// GetPostFormMap returns a map for a given form key, plus a boolean value
// whether at least one value exists for the given key.
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
req := c.Request
req.ParseForm()
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
dicts, exist := c.get(req.PostForm, key)

if !exist && req.MultipartForm != nil && req.MultipartForm.File != nil {
dicts, exist = c.get(req.MultipartForm.Value, key)
}

return dicts, exist
}

// get is an internal method and returns a map which satisfy conditions.
func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
dicts := make(map[string]string)
exist := false
for k, v := range m {
if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
exist = true
dicts[k[i+1:][:j]] = v[0]
}
}
}
return dicts, exist
}

// FormFile returns the first file for the provided form key.
func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
_, fh, err := c.Request.FormFile(name)
Expand Down
45 changes: 44 additions & 1 deletion context_test.go
Expand Up @@ -47,6 +47,8 @@ func createMultipartRequest() *http.Request {
must(mw.WriteField("time_local", "31/12/2016 14:55"))
must(mw.WriteField("time_utc", "31/12/2016 14:55"))
must(mw.WriteField("time_location", "31/12/2016 14:55"))
must(mw.WriteField("names[a]", "thinkerou"))
must(mw.WriteField("names[b]", "tianou"))
req, err := http.NewRequest("POST", "/", body)
must(err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
Expand Down Expand Up @@ -371,7 +373,8 @@ func TestContextQuery(t *testing.T) {
func TestContextQueryAndPostForm(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
c.Request, _ = http.NewRequest("POST", "/?both=GET&id=main&id=omit&array[]=first&array[]=second", body)
c.Request, _ = http.NewRequest("POST",
"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
c.Request.Header.Add("Content-Type", MIMEPOSTForm)

assert.Equal(t, "bar", c.DefaultPostForm("foo", "none"))
Expand Down Expand Up @@ -439,6 +442,30 @@ func TestContextQueryAndPostForm(t *testing.T) {
values = c.QueryArray("both")
assert.Equal(t, 1, len(values))
assert.Equal(t, "GET", values[0])

dicts, ok := c.GetQueryMap("ids")
assert.True(t, ok)
assert.Equal(t, "hi", dicts["a"])
assert.Equal(t, "3.14", dicts["b"])

dicts, ok = c.GetQueryMap("nokey")
assert.False(t, ok)
assert.Equal(t, 0, len(dicts))

dicts, ok = c.GetQueryMap("both")
assert.False(t, ok)
assert.Equal(t, 0, len(dicts))

dicts, ok = c.GetQueryMap("array")
assert.False(t, ok)
assert.Equal(t, 0, len(dicts))

dicts = c.QueryMap("ids")
assert.Equal(t, "hi", dicts["a"])
assert.Equal(t, "3.14", dicts["b"])

dicts = c.QueryMap("nokey")
assert.Equal(t, 0, len(dicts))
}

func TestContextPostFormMultipart(t *testing.T) {
Expand Down Expand Up @@ -515,6 +542,22 @@ func TestContextPostFormMultipart(t *testing.T) {
values = c.PostFormArray("foo")
assert.Equal(t, 1, len(values))
assert.Equal(t, "bar", values[0])

dicts, ok := c.GetPostFormMap("names")
assert.True(t, ok)
assert.Equal(t, "thinkerou", dicts["a"])
assert.Equal(t, "tianou", dicts["b"])

dicts, ok = c.GetPostFormMap("nokey")
assert.False(t, ok)
assert.Equal(t, 0, len(dicts))

dicts = c.PostFormMap("names")
assert.Equal(t, "thinkerou", dicts["a"])
assert.Equal(t, "tianou", dicts["b"])

dicts = c.PostFormMap("nokey")
assert.Equal(t, 0, len(dicts))
}

func TestContextSetCookie(t *testing.T) {
Expand Down