Support map as query string or post form parameters (#1383)
* support query map * add GetQueryMap and unittest * support post-form map * add readme for query map * attempt to fix bug for post-form map when go version is 1.6 * remove duplicate code * remove comment
This commit is contained in:
		
							
								
								
									
										29
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								README.md
									
									
									
									
									
								
							@ -27,6 +27,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
 | 
				
			|||||||
    - [Querystring parameters](#querystring-parameters)
 | 
					    - [Querystring parameters](#querystring-parameters)
 | 
				
			||||||
    - [Multipart/Urlencoded Form](#multiparturlencoded-form)
 | 
					    - [Multipart/Urlencoded Form](#multiparturlencoded-form)
 | 
				
			||||||
    - [Another example: query + post form](#another-example-query--post-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)
 | 
					    - [Upload files](#upload-files)
 | 
				
			||||||
    - [Grouping routes](#grouping-routes)
 | 
					    - [Grouping routes](#grouping-routes)
 | 
				
			||||||
    - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default)
 | 
					    - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default)
 | 
				
			||||||
@ -323,6 +324,34 @@ func main() {
 | 
				
			|||||||
id: 1234; page: 1; name: manu; message: this_is_great
 | 
					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
 | 
					### Upload files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Single file
 | 
					#### Single file
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										48
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								context.go
									
									
									
									
									
								
							@ -360,6 +360,18 @@ func (c *Context) GetQueryArray(key string) ([]string, bool) {
 | 
				
			|||||||
	return []string{}, false
 | 
						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
 | 
					// PostForm returns the specified key from a POST urlencoded form or multipart form
 | 
				
			||||||
// when it exists, otherwise it returns an empty string `("")`.
 | 
					// when it exists, otherwise it returns an empty string `("")`.
 | 
				
			||||||
func (c *Context) PostForm(key string) string {
 | 
					func (c *Context) PostForm(key string) string {
 | 
				
			||||||
@ -415,6 +427,42 @@ func (c *Context) GetPostFormArray(key string) ([]string, bool) {
 | 
				
			|||||||
	return []string{}, false
 | 
						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.
 | 
					// FormFile returns the first file for the provided form key.
 | 
				
			||||||
func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
 | 
					func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
 | 
				
			||||||
	_, fh, err := c.Request.FormFile(name)
 | 
						_, fh, err := c.Request.FormFile(name)
 | 
				
			||||||
 | 
				
			|||||||
@ -47,6 +47,8 @@ func createMultipartRequest() *http.Request {
 | 
				
			|||||||
	must(mw.WriteField("time_local", "31/12/2016 14:55"))
 | 
						must(mw.WriteField("time_local", "31/12/2016 14:55"))
 | 
				
			||||||
	must(mw.WriteField("time_utc", "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("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)
 | 
						req, err := http.NewRequest("POST", "/", body)
 | 
				
			||||||
	must(err)
 | 
						must(err)
 | 
				
			||||||
	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 | 
						req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 | 
				
			||||||
@ -371,7 +373,8 @@ func TestContextQuery(t *testing.T) {
 | 
				
			|||||||
func TestContextQueryAndPostForm(t *testing.T) {
 | 
					func TestContextQueryAndPostForm(t *testing.T) {
 | 
				
			||||||
	c, _ := CreateTestContext(httptest.NewRecorder())
 | 
						c, _ := CreateTestContext(httptest.NewRecorder())
 | 
				
			||||||
	body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
 | 
						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)
 | 
						c.Request.Header.Add("Content-Type", MIMEPOSTForm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t, "bar", c.DefaultPostForm("foo", "none"))
 | 
						assert.Equal(t, "bar", c.DefaultPostForm("foo", "none"))
 | 
				
			||||||
@ -439,6 +442,30 @@ func TestContextQueryAndPostForm(t *testing.T) {
 | 
				
			|||||||
	values = c.QueryArray("both")
 | 
						values = c.QueryArray("both")
 | 
				
			||||||
	assert.Equal(t, 1, len(values))
 | 
						assert.Equal(t, 1, len(values))
 | 
				
			||||||
	assert.Equal(t, "GET", values[0])
 | 
						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) {
 | 
					func TestContextPostFormMultipart(t *testing.T) {
 | 
				
			||||||
@ -515,6 +542,22 @@ func TestContextPostFormMultipart(t *testing.T) {
 | 
				
			|||||||
	values = c.PostFormArray("foo")
 | 
						values = c.PostFormArray("foo")
 | 
				
			||||||
	assert.Equal(t, 1, len(values))
 | 
						assert.Equal(t, 1, len(values))
 | 
				
			||||||
	assert.Equal(t, "bar", values[0])
 | 
						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) {
 | 
					func TestContextSetCookie(t *testing.T) {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user