Skip to content

Commit

Permalink
En/add support for encoded forms (#1073)
Browse files Browse the repository at this point in the history
  • Loading branch information
Umang01-hash authored Oct 7, 2024
1 parent 9190542 commit daf5a71
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 9 deletions.
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
```

- `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)
}
}

0 comments on commit daf5a71

Please sign in to comment.