From f70652221f3258b2de65c2e95a8c66ca04414b3e Mon Sep 17 00:00:00 2001 From: umang01-hash Date: Thu, 3 Oct 2024 14:36:10 +0530 Subject: [PATCH 1/5] initial commit --- pkg/gofr/http/request.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/pkg/gofr/http/request.go b/pkg/gofr/http/request.go index e5aa8971c..5a33aff38 100644 --- a/pkg/gofr/http/request.go +++ b/pkg/gofr/http/request.go @@ -68,6 +68,8 @@ func (r *Request) Bind(i interface{}) error { return json.Unmarshal(body, &i) case "multipart/form-data": return r.bindMultipart(i) + case "application/x-www-form-urlencoded": + return r.bindFormURLEncoded(i) } return nil @@ -122,7 +124,7 @@ func (r *Request) bindMultipart(ptr any) error { fd := formData{files: r.req.MultipartForm.File, fields: r.req.MultipartForm.Value} - ok, err := fd.mapStruct(ptrVal, &reflect.StructField{}) + ok, err := fd.mapStruct(ptrVal) if err != nil { return err } @@ -133,3 +135,29 @@ func (r *Request) bindMultipart(ptr any) error { return nil } + +func (r *Request) bindFormURLEncoded(ptr any) error { + ptrVal := reflect.ValueOf(ptr) + if ptrVal.Kind() != reflect.Ptr { + return errNonPointerBind + } + + ptrVal = ptrVal.Elem() + + // ParseForm populates r.req.Form + if err := r.req.ParseForm(); err != nil { + return err + } + + uf := formData{fields: r.req.Form} + ok, err := uf.mapStruct(ptrVal) + if err != nil { + return err + } + + if !ok { + return errFieldsNotSet + } + + return nil +} From 56f45be11de74f97ce5f1cbcef5400f3396cfd08 Mon Sep 17 00:00:00 2001 From: umang01-hash Date: Thu, 3 Oct 2024 15:31:47 +0530 Subject: [PATCH 2/5] add support for url encoded form --- pkg/gofr/http/multipart_file_bind.go | 14 ++++++++++++++ pkg/gofr/http/request.go | 5 +++-- pkg/gofr/http/request_test.go | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/pkg/gofr/http/multipart_file_bind.go b/pkg/gofr/http/multipart_file_bind.go index a4fe481f6..571ceac1b 100644 --- a/pkg/gofr/http/multipart_file_bind.go +++ b/pkg/gofr/http/multipart_file_bind.go @@ -29,6 +29,16 @@ type formData struct { func (uf *formData) mapStruct(val reflect.Value, field *reflect.StructField) (bool, error) { vKind := val.Kind() + if field == nil { + // Check if val is not a struct + if vKind != reflect.Struct { + return false, nil // Return false if val is not a struct + } + + // If field is nil, iterate through all fields of the struct + return uf.iterateStructFields(val) + } + if vKind == reflect.Pointer { return uf.checkPointer(val, field) } @@ -79,6 +89,10 @@ func (uf *formData) checkPointer(val reflect.Value, field *reflect.StructField) } func (uf *formData) checkStruct(val reflect.Value) (bool, error) { + return uf.iterateStructFields(val) +} + +func (uf *formData) iterateStructFields(val reflect.Value) (bool, error) { var set bool tVal := val.Type() diff --git a/pkg/gofr/http/request.go b/pkg/gofr/http/request.go index 5a33aff38..01d4691a7 100644 --- a/pkg/gofr/http/request.go +++ b/pkg/gofr/http/request.go @@ -124,7 +124,7 @@ func (r *Request) bindMultipart(ptr any) error { fd := formData{files: r.req.MultipartForm.File, fields: r.req.MultipartForm.Value} - ok, err := fd.mapStruct(ptrVal) + ok, err := fd.mapStruct(ptrVal, nil) if err != nil { return err } @@ -150,7 +150,8 @@ func (r *Request) bindFormURLEncoded(ptr any) error { } uf := formData{fields: r.req.Form} - ok, err := uf.mapStruct(ptrVal) + + ok, err := uf.mapStruct(ptrVal, nil) if err != nil { return err } diff --git a/pkg/gofr/http/request_test.go b/pkg/gofr/http/request_test.go index 054fe7867..d69832c87 100644 --- a/pkg/gofr/http/request_test.go +++ b/pkg/gofr/http/request_test.go @@ -251,3 +251,24 @@ func Test_Params(t *testing.T) { assert.ElementsMatch(t, expectedTags, r.Params("tag"), "expected all values of 'tag' to match") assert.Empty(t, r.Params("nonexistent"), "expected empty slice for non-existent query param") } + +func TestBind_FormURLEncoded(t *testing.T) { + // Create a new HTTP request with form-encoded data + req := NewRequest(httptest.NewRequest(http.MethodPost, "/abc", strings.NewReader("Name=John&Age=30"))) + req.req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + x := struct { + Name string `form:"Name"` + Age int `form:"Age"` + }{} + + err := req.Bind(&x) + if err != nil { + t.Errorf("Bind error: %v", err) + } + + // Check the results + if x.Name != "John" || x.Age != 30 { + t.Errorf("Bind error. Got: %v", x) + } +} From 95e0ce78f96098de071669ffeaf1328f72024c39 Mon Sep 17 00:00:00 2001 From: umang01-hash Date: Thu, 3 Oct 2024 16:17:18 +0530 Subject: [PATCH 3/5] add support in documentation --- docs/references/context/page.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/context/page.md b/docs/references/context/page.md index fb0da0925..41d03be42 100644 --- a/docs/references/context/page.md +++ b/docs/references/context/page.md @@ -51,7 +51,7 @@ parts of the request. // the Bind() method will map the incoming request to variable p ``` -- `Binding multipart-form data` +- `Binding multipart-form data / urlencoded form data ` - To bind multipart-form data, you can use the Bind method similarly. The struct fields should be tagged appropriately to map the form fields to the struct fields. From d35b40723b144e0f3d11961bf1f9efab6da66ec6 Mon Sep 17 00:00:00 2001 From: umang01-hash Date: Fri, 4 Oct 2024 10:03:05 +0530 Subject: [PATCH 4/5] refactor request.go for handling multipart and encoded form --- pkg/gofr/http/request.go | 53 ++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/pkg/gofr/http/request.go b/pkg/gofr/http/request.go index 01d4691a7..2c493a7f6 100644 --- a/pkg/gofr/http/request.go +++ b/pkg/gofr/http/request.go @@ -111,32 +111,14 @@ func (r *Request) body() ([]byte, error) { } func (r *Request) bindMultipart(ptr any) error { - ptrVal := reflect.ValueOf(ptr) - if ptrVal.Kind() != reflect.Ptr { - return errNonPointerBind - } - - ptrVal = ptrVal.Elem() - - if err := r.req.ParseMultipartForm(defaultMaxMemory); err != nil { - return err - } - - fd := formData{files: r.req.MultipartForm.File, fields: r.req.MultipartForm.Value} - - ok, err := fd.mapStruct(ptrVal, nil) - if err != nil { - return err - } - - if !ok { - return errNoFileFound - } - - return nil + return r.bindForm(ptr, true) } func (r *Request) bindFormURLEncoded(ptr any) error { + return r.bindForm(ptr, false) +} + +func (r *Request) bindForm(ptr any, isMultipart bool) error { ptrVal := reflect.ValueOf(ptr) if ptrVal.Kind() != reflect.Ptr { return errNonPointerBind @@ -144,19 +126,32 @@ func (r *Request) bindFormURLEncoded(ptr any) error { ptrVal = ptrVal.Elem() - // ParseForm populates r.req.Form - if err := r.req.ParseForm(); err != nil { - return err - } + var fd formData - uf := formData{fields: r.req.Form} + if isMultipart { + if err := r.req.ParseMultipartForm(defaultMaxMemory); err != nil { + return err + } - ok, err := uf.mapStruct(ptrVal, nil) + fd = formData{files: r.req.MultipartForm.File, fields: r.req.MultipartForm.Value} + } else { + if err := r.req.ParseForm(); err != nil { + return err + } + + fd = formData{fields: r.req.Form} + } + + ok, err := fd.mapStruct(ptrVal, nil) if err != nil { return err } if !ok { + if isMultipart { + return errNoFileFound + } + return errFieldsNotSet } From 7a2d17d382db824097b8d4ffc3e3ea73b8fd142d Mon Sep 17 00:00:00 2001 From: umang01-hash Date: Fri, 4 Oct 2024 15:31:40 +0530 Subject: [PATCH 5/5] update documentation --- docs/references/context/page.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/references/context/page.md b/docs/references/context/page.md index 41d03be42..ec34e03c3 100644 --- a/docs/references/context/page.md +++ b/docs/references/context/page.md @@ -52,8 +52,8 @@ parts of the request. ``` - `Binding multipart-form data / urlencoded form data ` - - To bind multipart-form data, you can use the Bind method similarly. The struct fields should be tagged appropriately - to map the form fields to the struct fields. + - To bind multipart-form data or url-encoded form, you can use the Bind method similarly. The struct fields should be tagged appropriately + to map the form fields to the struct fields. The supported content types are `multipart/form-data` and `application/x-www-form-urlencoded` ```go type Data struct {