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

refactor(form_mapping.go): mapping ptr, struct and map #1749

Merged
merged 10 commits into from
Mar 3, 2019
18 changes: 0 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1834,24 +1834,6 @@ $ 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 have form
}

type StructZ struct {
Z *StructZ `form:"name_z"` // HERE have form
}
```

In a word, only support nested custom struct which have no `form` now.

### Try to bind body into different structs

The normal methods for binding request body consumes `c.Request.Body` and they
Expand Down
134 changes: 115 additions & 19 deletions binding/binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"mime/multipart"
"net/http"
"strconv"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -57,7 +58,6 @@ type FooStructForTimeTypeFailLocation struct {
}

type FooStructForMapType struct {
// Unknown type: not support map
MapFoo map[string]interface{} `form:"map_foo"`
}

Expand Down Expand Up @@ -303,7 +303,7 @@ func TestBindingFormInvalidName2(t *testing.T) {
func TestBindingFormForType(t *testing.T) {
testFormBindingForType(t, "POST",
"/", "/",
"map_foo=", "bar2=1", "Map")
"map_foo={\"bar\":123}", "map_foo=1", "Map")

testFormBindingForType(t, "POST",
"/", "/",
Expand Down Expand Up @@ -508,20 +508,30 @@ func TestBindingYAMLFail(t *testing.T) {
`foo:\nbar`, `bar: foo`)
}

func createFormPostRequest() *http.Request {
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
func createFormPostRequest(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}

func createDefaultFormPostRequest() *http.Request {
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar"))
func createDefaultFormPostRequest(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}

func createFormPostRequestFail() *http.Request {
req, _ := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=bar"))
func createFormPostRequestForMap(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}

func createFormPostRequestForMapFail(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
Expand All @@ -535,26 +545,42 @@ func createFormMultipartRequest(t *testing.T) *http.Request {
assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("foo", "bar"))
assert.NoError(t, mw.WriteField("bar", "foo"))
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}

func createFormMultipartRequestFail(t *testing.T) *http.Request {
func createFormMultipartRequestForMap(t *testing.T) *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()

assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("map_foo", "bar"))
req, _ := http.NewRequest("POST", "/?map_foo=getfoo", body)
assert.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}

func createFormMultipartRequestForMapFail(t *testing.T) *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()

assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("map_foo", "3.14"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}

func TestBindingFormPost(t *testing.T) {
req := createFormPostRequest()
req := createFormPostRequest(t)
var obj FooBarStruct
assert.NoError(t, FormPost.Bind(req, &obj))

Expand All @@ -564,16 +590,24 @@ func TestBindingFormPost(t *testing.T) {
}

func TestBindingDefaultValueFormPost(t *testing.T) {
req := createDefaultFormPostRequest()
req := createDefaultFormPostRequest(t)
var obj FooDefaultBarStruct
assert.NoError(t, FormPost.Bind(req, &obj))

assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, "hello", obj.Bar)
}

func TestBindingFormPostFail(t *testing.T) {
req := createFormPostRequestFail()
func TestBindingFormPostForMap(t *testing.T) {
req := createFormPostRequestForMap(t)
var obj FooStructForMapType
err := FormPost.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
}

func TestBindingFormPostForMapFail(t *testing.T) {
req := createFormPostRequestForMapFail(t)
var obj FooStructForMapType
err := FormPost.Bind(req, &obj)
assert.Error(t, err)
Expand All @@ -589,8 +623,18 @@ func TestBindingFormMultipart(t *testing.T) {
assert.Equal(t, "foo", obj.Bar)
}

func TestBindingFormMultipartFail(t *testing.T) {
req := createFormMultipartRequestFail(t)
func TestBindingFormMultipartForMap(t *testing.T) {
req := createFormMultipartRequestForMap(t)
var obj FooStructForMapType
err := FormMultipart.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
assert.Equal(t, "thinkerou", obj.MapFoo["name"].(string))
assert.Equal(t, float64(3.14), obj.MapFoo["pai"].(float64))
}

func TestBindingFormMultipartForMapFail(t *testing.T) {
req := createFormMultipartRequestForMapFail(t)
var obj FooStructForMapType
err := FormMultipart.Bind(req, &obj)
assert.Error(t, err)
Expand Down Expand Up @@ -773,6 +817,17 @@ func TestFormBindingFail(t *testing.T) {
assert.Error(t, err)
}

func TestFormBindingMultipartFail(t *testing.T) {
obj := FooBarStruct{}
req, err := http.NewRequest("POST", "/", strings.NewReader("foo=bar"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary")
_, err = req.MultipartReader()
assert.NoError(t, err)
err = Form.Bind(req, &obj)
assert.Error(t, err)
}

func TestFormPostBindingFail(t *testing.T) {
b := FormPost
assert.Equal(t, "form-urlencoded", b.Name())
Expand Down Expand Up @@ -1109,7 +1164,8 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
case "Map":
obj := FooStructForMapType{}
err := b.Bind(req, &obj)
assert.Error(t, err)
assert.NoError(t, err)
assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
case "SliceMap":
obj := FooStructForSliceMapType{}
err := b.Bind(req, &obj)
Expand Down Expand Up @@ -1317,3 +1373,43 @@ func TestCanSet(t *testing.T) {
var c CanSetStruct
assert.Nil(t, mapForm(&c, nil))
}

func formPostRequest(path, body string) *http.Request {
req := requestWithBody("POST", path, body)
req.Header.Add("Content-Type", MIMEPOSTForm)
return req
}

func TestBindingSliceDefault(t *testing.T) {
var s struct {
Friends []string `form:"friends,default=mike"`
}
req := formPostRequest("", "")
err := Form.Bind(req, &s)
assert.NoError(t, err)

assert.Len(t, s.Friends, 1)
assert.Equal(t, "mike", s.Friends[0])
}

func TestBindingStructField(t *testing.T) {
var s struct {
Opts struct {
Port int
} `form:"opts"`
}
req := formPostRequest("", `opts={"Port": 8000}`)
err := Form.Bind(req, &s)
assert.NoError(t, err)
assert.Equal(t, 8000, s.Opts.Port)
}

func TestBindingUnknownTypeChan(t *testing.T) {
var s struct {
Stop chan bool `form:"stop"`
}
req := formPostRequest("", "stop=true")
err := Form.Bind(req, &s)
assert.Error(t, err)
assert.Equal(t, errUnknownType, err)
}
Loading