From 5b9692dc3b78a7478f11adbd9ed11db97db745a8 Mon Sep 17 00:00:00 2001 From: guonaihong Date: Tue, 13 Aug 2019 21:49:41 +0800 Subject: [PATCH 1/5] binding: support []byte * form1.go ```golang package main import ( "fmt" "github.com/gin-gonic/gin" ) type testForm struct { Mode string `form:"mode"` Text string `form:"text"` Voice []byte `form:"voice"` Voice2 []byte `form:"voice2"` } func main() { router := gin.Default() router.POST("/test.form", func(c *gin.Context) { t2 := testForm{} if err := c.ShouldBind(&t2); err != nil { fmt.Printf("err = %s:%v\n", err, t2) c.JSON(500, gin.H{"err": err.Error()}) return } c.JSON(200, t2) }) router.Run() } //client /* curl -F mode=A -F text="test" -F voice=@form1.go -F voice2="voice" 127.0.0.1:8080/test.form|jq { "Mode": "A", "Text": "test", "Voice": "cGFja2FnZSBtYWluCgppbXBvcnQgKAoJImZtdCIKCSJnaXRodWIuY29tL2dpbi1nb25pYy9naW4iCikKCnR5cGUgdGVzdEZvcm0gc3RydWN0IHsKCU1vZGUgICBzdHJpbmcgYGZvcm06Im1vZGUiYAoJVGV4dCAgIHN0cmluZyBgZm9ybToidGV4dCJgCglWb2ljZSAgW11ieXRlIGBmb3JtOiJ2b2ljZSJgCglWb2ljZTIgW11ieXRlIGBmb3JtOiJ2b2ljZTIiYAp9CgpmdW5jIG1haW4oKSB7Cglyb3V0ZXIgOj0gZ2luLkRlZmF1bHQoKQoJcm91dGVyLlBPU1QoIi90ZXN0LmZvcm0iLCBmdW5jKGMgKmdpbi5Db250ZXh0KSB7CgoJCXQyIDo9IHRlc3RGb3Jte30KCQlpZiBlcnIgOj0gYy5TaG91bGRCaW5kKCZ0Mik7IGVyciAhPSBuaWwgewoJCQlmbXQuUHJpbnRmKCJlcnIgPSAlczoldlxuIiwgZXJyLCB0MikKCQkJYy5KU09OKDUwMCwgZ2luLkh7ImVyciI6IGVyci5FcnJvcigpfSkKCQkJcmV0dXJuCgkJfQoJCWMuSlNPTigyMDAsIHQyKQoJfSkKCglyb3V0ZXIuUnVuKCkKfQo=", "Voice2": "dm9pY2U=" } */ ``` --- binding/form_mapping.go | 9 +++++++++ binding/multipart_form_mapping.go | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 80b1d15a..7d6075b4 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -146,6 +146,15 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ if !ok { vs = []string{opt.defaultValue} } + + switch value.Interface().(type) { + case []byte: + if len(vs) > 0 { + value.Set(reflect.ValueOf([]byte(vs[0]))) + return true, nil + } + } + return true, setSlice(vs, value, field) case reflect.Array: if !ok { diff --git a/binding/multipart_form_mapping.go b/binding/multipart_form_mapping.go index f85a1aa6..1b2ad001 100644 --- a/binding/multipart_form_mapping.go +++ b/binding/multipart_form_mapping.go @@ -6,6 +6,7 @@ package binding import ( "errors" + "io/ioutil" "mime/multipart" "net/http" "reflect" @@ -39,6 +40,22 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file return true, nil } case reflect.Slice: + switch value.Interface().(type) { + case []byte: + fd, err := files[0].Open() + if err != nil { + return false, err + } + defer fd.Close() + c, err := ioutil.ReadAll(fd) + if err != nil { + return false, err + } + + value.Set(reflect.ValueOf(c)) + return true, nil + } + slice := reflect.MakeSlice(value.Type(), len(files), len(files)) isSetted, err = setArrayOfMultipartFormFiles(slice, field, files) if err != nil || !isSetted { From 895518e3b01b8757e54add347e4534a2e8f983f8 Mon Sep 17 00:00:00 2001 From: guonaihong Date: Wed, 14 Aug 2019 21:23:01 +0800 Subject: [PATCH 2/5] add test function --- binding/multipart_form_mapping_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 4c75d1fe..60b5cbfe 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -14,6 +14,19 @@ import ( "github.com/stretchr/testify/assert" ) +func TestFormMultipartBindingOneFileToBytesArrayFail(t *testing.T) { + var test struct { + Voice []byte `form:"voice"` + } + + file := testFile{"voice", "test.pcm", []byte("pcm pcm pcm")} + req := createRequestMultipartFiles(t, file) + err := FormMultipart.Bind(req, &test) + assert.NoError(t, err) + + assert.Equal(t, test.Voice, []byte("pcm pcm pcm")) +} + func TestFormMultipartBindingBindOneFile(t *testing.T) { var s struct { FileValue multipart.FileHeader `form:"file"` From 93567f91e679dc2c83773f1d539b4b3edd250ffb Mon Sep 17 00:00:00 2001 From: guonaihong Date: Wed, 14 Aug 2019 21:31:59 +0800 Subject: [PATCH 3/5] add test function --- binding/form_mapping_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index c9d6111b..772ac144 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -260,6 +260,15 @@ func TestMappingStructField(t *testing.T) { assert.Equal(t, 9, s.J.I) } +func TestByteArray(t *testing.T) { + var s struct { + B []byte + } + err := mappingByPtr(&s, formSource{"B": {"hello"}}, "form") + assert.NoError(t, err) + assert.Equal(t, []byte("hello"), s.B) +} + func TestMappingMapField(t *testing.T) { var s struct { M map[string]int From 27a8f773115045125d790877dcf55468c71688e9 Mon Sep 17 00:00:00 2001 From: guonaihong Date: Thu, 15 Aug 2019 21:17:07 +0800 Subject: [PATCH 4/5] add test function --- binding/multipart_form_mapping_test.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 60b5cbfe..1888862f 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -14,7 +14,25 @@ import ( "github.com/stretchr/testify/assert" ) -func TestFormMultipartBindingOneFileToBytesArrayFail(t *testing.T) { +func TestFormMultipartBindingOneFileToBytesFail1(t *testing.T) { + var test struct { + Voice []byte `form:"voice"` + } + + file := testFile{"voice", "test.pcm", []byte("pcm pcm pcm")} + req := createRequestMultipartFiles(t, file) + + err := req.ParseMultipartForm(3) + assert.NoError(t, err) + + err = req.MultipartForm.RemoveAll() + assert.NoError(t, err) + + err = mappingByPtr(&test, (*multipartRequest)(req), "form") + assert.Error(t, err) +} + +func TestFormMultipartBindingOneFileToBytesArray(t *testing.T) { var test struct { Voice []byte `form:"voice"` } From 24943ac009a7cd372bbd3c9eb586b66c94f8368f Mon Sep 17 00:00:00 2001 From: guonaihong Date: Mon, 19 Aug 2019 12:58:29 +0800 Subject: [PATCH 5/5] add test function --- binding/multipart_form_mapping.go | 6 ++++++ binding/multipart_form_mapping_test.go | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/binding/multipart_form_mapping.go b/binding/multipart_form_mapping.go index 1b2ad001..c830359e 100644 --- a/binding/multipart_form_mapping.go +++ b/binding/multipart_form_mapping.go @@ -15,6 +15,7 @@ import ( type multipartRequest http.Request var _ setter = (*multipartRequest)(nil) +var ConstructionFailure = false // TrySet tries to set a value by the multipart request with the binding a form file func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) { @@ -48,6 +49,11 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file } defer fd.Close() c, err := ioutil.ReadAll(fd) + + if ConstructionFailure { + err = errors.New("test use") + } + if err != nil { return false, err } diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go index 1888862f..08c9fc9e 100644 --- a/binding/multipart_form_mapping_test.go +++ b/binding/multipart_form_mapping_test.go @@ -32,6 +32,24 @@ func TestFormMultipartBindingOneFileToBytesFail1(t *testing.T) { assert.Error(t, err) } +func TestFormMultipartBindingOneFileToBytesFail2(t *testing.T) { + var test struct { + Voice []byte `form:"voice"` + } + + file := testFile{"voice", "test.pcm", []byte("pcm pcm pcm")} + req := createRequestMultipartFiles(t, file) + + ConstructionFailure = true + + err := req.ParseMultipartForm(3) + assert.NoError(t, err) + + err = mappingByPtr(&test, (*multipartRequest)(req), "form") + assert.Error(t, err) + ConstructionFailure = false +} + func TestFormMultipartBindingOneFileToBytesArray(t *testing.T) { var test struct { Voice []byte `form:"voice"`