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

En/add support for encoded forms #1073

Open
wants to merge 8 commits into
base: development
Choose a base branch
from
6 changes: 3 additions & 3 deletions docs/references/context/page.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ parts of the request.
// the Bind() method will map the incoming request to variable p
```

aryanmehrotra marked this conversation as resolved.
Show resolved Hide resolved
- `Binding multipart-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.
- `Binding multipart-form data / urlencoded form data `
- 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 {
Expand Down
14 changes: 14 additions & 0 deletions pkg/gofr/http/multipart_file_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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()
Expand Down
36 changes: 30 additions & 6 deletions pkg/gofr/http/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -109,26 +111,48 @@ func (r *Request) body() ([]byte, error) {
}

func (r *Request) bindMultipart(ptr any) error {
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
}

ptrVal = ptrVal.Elem()

if err := r.req.ParseMultipartForm(defaultMaxMemory); err != nil {
return err
}
var fd formData

fd := formData{files: r.req.MultipartForm.File, fields: r.req.MultipartForm.Value}
if isMultipart {
if err := r.req.ParseMultipartForm(defaultMaxMemory); err != nil {
return err
}

ok, err := fd.mapStruct(ptrVal, &reflect.StructField{})
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 {
return errNoFileFound
if isMultipart {
return errNoFileFound
}

return errFieldsNotSet
}

return nil
Expand Down
21 changes: 21 additions & 0 deletions pkg/gofr/http/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Loading