Skip to content

Instantly share code, notes, and snippets.

@dipeshdulal
Last active May 21, 2021 14:59
Show Gist options
  • Save dipeshdulal/2d14c372f18c1ff7b9f0c3a7c67f8da6 to your computer and use it in GitHub Desktop.
Save dipeshdulal/2d14c372f18c1ff7b9f0c3a7c67f8da6 to your computer and use it in GitHub Desktop.
Custom Binding Multipart Form Data
package utils
import (
"clean-architecture/models"
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
"strconv"
"time"
)
// CustomBind custom bind the data
func CustomBind(source *http.Request, dest interface{}) error {
source.ParseMultipartForm(1000)
if source == nil {
return nil
}
formSource := source.MultipartForm.Value
destValue := reflect.ValueOf(dest)
destType := reflect.TypeOf(dest)
if destType.Kind() != reflect.Ptr {
return errors.New("only pointers can be binded")
}
if destType.Elem().Kind() != reflect.Struct {
return errors.New("only struct pointers are allowed")
}
for i := 0; i < destType.Elem().NumField(); i++ {
currentField := destType.Elem().Field(i)
fieldValue := destValue.Elem().Field(i)
if currentField.Type == reflect.TypeOf(models.Base{}) {
if err := CustomBind(source, fieldValue.Addr().Interface()); err != nil {
return err
}
}
key := currentField.Tag.Get("form")
if key == "" {
continue
}
targetValue, ok := formSource[key]
if !ok {
continue
}
if len(targetValue) == 0 {
continue
}
kind := currentField.Type.Kind()
switch kind {
case reflect.String:
fieldValue.SetString(targetValue[0])
case reflect.Bool:
b, err := strconv.ParseBool(targetValue[0])
if err != nil {
return err
}
fieldValue.SetBool(b)
case reflect.Int:
i, err := strconv.Atoi(targetValue[0])
if err != nil {
return err
}
fieldValue.SetInt(int64(i))
default:
_, ok := fieldValue.Interface().(time.Time)
if ok {
val, _ := time.Parse("2006-01-02 15:04:05", targetValue[0])
fieldValue.Set(reflect.ValueOf(val))
continue
}
if fieldValue.CanInterface() && fieldValue.Type().NumMethod() > 0 {
val, ok := fieldValue.Addr().Interface().(json.Unmarshaler)
if !ok {
return fmt.Errorf("data type %s doesn't implement unmarshaler interface", fieldValue.Type())
}
err := val.UnmarshalJSON([]byte(targetValue[0]))
if err != nil {
return err
}
}
}
}
return nil
}

Custom Binding of Multipart Form Data

Gin Gonic doesn't handle binding of multipart form data with custom types. This provides basic implementation of binding the form data into a struct. This code can be modified to fit ones need.

Usage:

if err := utils.CustomBind(c.Request, &user); err != nil {
	u.logger.Zap.Error(err)
	c.JSON(http.StatusInternalServerError, gin.H{
		"error": err.Error(),
	})
	return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment