93
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								README.md
									
									
									
									
									
								
							@ -51,6 +51,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
 | 
			
		||||
    - [Run multiple service using Gin](#run-multiple-service-using-gin)
 | 
			
		||||
    - [Graceful restart or stop](#graceful-restart-or-stop)
 | 
			
		||||
    - [Build a single binary with templates](#build-a-single-binary-with-templates)
 | 
			
		||||
    - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct)
 | 
			
		||||
- [Testing](#testing)
 | 
			
		||||
- [Users](#users--)
 | 
			
		||||
 | 
			
		||||
@ -1461,6 +1462,98 @@ func loadTemplate() (*template.Template, error) {
 | 
			
		||||
 | 
			
		||||
See a complete example in the `examples/assets-in-binary` directory.
 | 
			
		||||
 | 
			
		||||
### Bind form-data request with custom struct
 | 
			
		||||
 | 
			
		||||
The follow example using custom struct:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type StructA struct {
 | 
			
		||||
    FieldA string `form:"field_a"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StructB struct {
 | 
			
		||||
    NestedStruct StructA
 | 
			
		||||
    FieldB string `form:"field_b"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StructC struct {
 | 
			
		||||
    NestedStructPointer *StructA
 | 
			
		||||
    FieldC string `form:"field_c"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StructD struct {
 | 
			
		||||
    NestedAnonyStruct struct {
 | 
			
		||||
        FieldX string `form:"field_x"`
 | 
			
		||||
    }
 | 
			
		||||
    FieldD string `form:"field_d"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDataB(c *gin.Context) {
 | 
			
		||||
    var b StructB
 | 
			
		||||
    c.Bind(&b)
 | 
			
		||||
    c.JSON(200, gin.H{
 | 
			
		||||
        "a": b.NestedStruct,
 | 
			
		||||
        "b": b.FieldB,
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDataC(c *gin.Context) {
 | 
			
		||||
    var b StructC
 | 
			
		||||
    c.Bind(&b)
 | 
			
		||||
    c.JSON(200, gin.H{
 | 
			
		||||
        "a": b.NestedStructPointer,
 | 
			
		||||
        "c": b.FieldC,
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDataD(c *gin.Context) {
 | 
			
		||||
    var b StructD
 | 
			
		||||
    c.Bind(&b)
 | 
			
		||||
    c.JSON(200, gin.H{
 | 
			
		||||
        "x": b.NestedAnonyStruct,
 | 
			
		||||
        "d": b.FieldD,
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
    r := gin.Default()
 | 
			
		||||
    r.GET("/getb", GetDataB)
 | 
			
		||||
    r.GET("/getc", GetDataC)
 | 
			
		||||
    r.GET("/getd", GetDataD)
 | 
			
		||||
 | 
			
		||||
    r.Run()
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Using the command `curl` command result:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
 | 
			
		||||
{"a":{"FieldA":"hello"},"b":"world"}
 | 
			
		||||
$ curl "http://localhost:8080/getc?field_a=hello&field_c=world"
 | 
			
		||||
{"a":{"FieldA":"hello"},"c":"world"}
 | 
			
		||||
$ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
 | 
			
		||||
{"d":"world","x":{"FieldX":"hello"}}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**NOTE**: NOT support the follow style struct:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type StructX struct {
 | 
			
		||||
    X struct {} `form:"name_x"` // HERE have form
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StructY struct {
 | 
			
		||||
    Y StructX `form:"name_y"` // HERE hava form
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StructZ struct {
 | 
			
		||||
    Z *StructZ `form:"name_z"` // HERE hava form
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In a word, only support nested custom struct which have no `form` now.
 | 
			
		||||
 | 
			
		||||
## Testing
 | 
			
		||||
 | 
			
		||||
The `net/http/httptest` package is preferable way for HTTP testing.
 | 
			
		||||
 | 
			
		||||
@ -74,6 +74,18 @@ type FooStructForSliceType struct {
 | 
			
		||||
	SliceFoo []int `form:"slice_foo"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FooStructForStructType struct {
 | 
			
		||||
	StructFoo struct {
 | 
			
		||||
		Idx int `form:"idx"`
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FooStructForStructPointerType struct {
 | 
			
		||||
	StructPointerFoo *struct {
 | 
			
		||||
		Name string `form:"name"`
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FooStructForSliceMapType struct {
 | 
			
		||||
	// Unknown type: not support map
 | 
			
		||||
	SliceMapFoo []map[string]interface{} `form:"slice_map_foo"`
 | 
			
		||||
@ -395,6 +407,22 @@ func TestBindingFormForType(t *testing.T) {
 | 
			
		||||
	testFormBindingForType(t, "GET",
 | 
			
		||||
		"/?ptr_bar=test", "/?bar2=test",
 | 
			
		||||
		"", "", "Ptr")
 | 
			
		||||
 | 
			
		||||
	testFormBindingForType(t, "POST",
 | 
			
		||||
		"/", "/",
 | 
			
		||||
		"idx=123", "id1=1", "Struct")
 | 
			
		||||
 | 
			
		||||
	testFormBindingForType(t, "GET",
 | 
			
		||||
		"/?idx=123", "/?id1=1",
 | 
			
		||||
		"", "", "Struct")
 | 
			
		||||
 | 
			
		||||
	testFormBindingForType(t, "POST",
 | 
			
		||||
		"/", "/",
 | 
			
		||||
		"name=thinkerou", "name1=ou", "StructPointer")
 | 
			
		||||
 | 
			
		||||
	testFormBindingForType(t, "GET",
 | 
			
		||||
		"/?name=thinkerou", "/?name1=ou",
 | 
			
		||||
		"", "", "StructPointer")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBindingQuery(t *testing.T) {
 | 
			
		||||
@ -953,6 +981,28 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
 | 
			
		||||
		req = requestWithBody(method, badPath, badBody)
 | 
			
		||||
		err = JSON.Bind(req, &obj)
 | 
			
		||||
		assert.Error(t, err)
 | 
			
		||||
	case "Struct":
 | 
			
		||||
		obj := FooStructForStructType{}
 | 
			
		||||
		err := b.Bind(req, &obj)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Equal(t,
 | 
			
		||||
			struct {
 | 
			
		||||
				Idx int "form:\"idx\""
 | 
			
		||||
			}(struct {
 | 
			
		||||
				Idx int "form:\"idx\""
 | 
			
		||||
			}{Idx: 123}),
 | 
			
		||||
			obj.StructFoo)
 | 
			
		||||
	case "StructPointer":
 | 
			
		||||
		obj := FooStructForStructPointerType{}
 | 
			
		||||
		err := b.Bind(req, &obj)
 | 
			
		||||
		assert.NoError(t, err)
 | 
			
		||||
		assert.Equal(t,
 | 
			
		||||
			struct {
 | 
			
		||||
				Name string "form:\"name\""
 | 
			
		||||
			}(struct {
 | 
			
		||||
				Name string "form:\"name\""
 | 
			
		||||
			}{Name: "thinkerou"}),
 | 
			
		||||
			*obj.StructPointerFoo)
 | 
			
		||||
	case "Map":
 | 
			
		||||
		obj := FooStructForMapType{}
 | 
			
		||||
		err := b.Bind(req, &obj)
 | 
			
		||||
 | 
			
		||||
@ -36,9 +36,16 @@ func mapForm(ptr interface{}, form map[string][]string) error {
 | 
			
		||||
		if inputFieldName == "" {
 | 
			
		||||
			inputFieldName = typeField.Name
 | 
			
		||||
 | 
			
		||||
			// if "form" tag is nil, we inspect if the field is a struct.
 | 
			
		||||
			// if "form" tag is nil, we inspect if the field is a struct or struct pointer.
 | 
			
		||||
			// this would not make sense for JSON parsing but it does for a form
 | 
			
		||||
			// since data is flatten
 | 
			
		||||
			if structFieldKind == reflect.Ptr {
 | 
			
		||||
				if !structField.Elem().IsValid() {
 | 
			
		||||
					structField.Set(reflect.New(structField.Type().Elem()))
 | 
			
		||||
				}
 | 
			
		||||
				structField = structField.Elem()
 | 
			
		||||
				structFieldKind = structField.Kind()
 | 
			
		||||
			}
 | 
			
		||||
			if structFieldKind == reflect.Struct {
 | 
			
		||||
				err := mapForm(structField.Addr().Interface(), form)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user