Description
Binding uuid.UUID using ShouldBind gives error ["45e1f85e-bca5-458d-bd9c-c56edd8f847b"] is not valid value for uuid.UUID
How to reproduce
Used Postman to make request: Content-Type: multipart/form-data Body with form-data: key: colorID value: 45e1f85e-bca5-458d-bd9c-c56edd8f847b
// Go model in which I want to map
type Test struct {
colorID uuid.UUID `binding:"required" form:"colorID"`
}
func test(ctx *gin.Context) {
t := Test{}
err := ctx.ShouldBind(&t)
if err != nil {
panic(err)
}
}
Actual result
["45e1f85e-bca5-458d-bd9c-c56edd8f847b"] is not valid value for uuid.UUID
Comment From: quentinvernot
I have the same issue when attempting to bind with url-safe base64 values, I couldn't find a way to define a custom type handle the binding and error handling on its own and had to basically decode the string and handle the error in every handler. it's possible to use custom types when using json.Unmarshal
, but, even though ShouldBind
falls back to using enconding/json
as well, the input value isn't json in those cases (it's a string for uuid.UUID
and my case), and the binding fails.
Maybe TextUnmarshaler
could be used as another fallback? uuid.UUID
implements the UnmarshalText
method, and it would be easy to do for custom types too.
It could be seen as API-breaking though, since TextUnmarshaler
is a pretty common interface, people using ShouldBind
to bind to structs with non-primitive fields may have surprises.
Comment From: vivl4725
I have this problem too, but I found solution!
You can change uuid.UUID to string and place in binding uuid tag!
Comment From: damarowen
If you use github.com/google/uuid package, you can use uuid.Parse
example: _ID , err := uuid.Parse(dto.ID) c, err := s.CampaignRepository.FindById(_ID)
reference : https://stackoverflow.com/questions/62952279/how-to-convert-a-uuid-string-into-uuid-type
Comment From: hungtcs
+1. Any way to serialization and deserialization uri bindding or query bindding?
Comment From: AnatolyUA
This function can be used to try to assign UUID to reflect.Value.
package binding
import (
uuid "github.com/satori/go.uuid"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
)
func tryAssignUUID(s string, value reflect.Value) bool {
l := len(s)
if value.Kind() == reflect.Array &&
value.Len() == 16 &&
value.Index(0).Kind() == reflect.Uint8 &&
(l == 32 || l == 36 || l == 38 || l == 41 || l == 45) {
if uid, uErr := uuid.FromString(s); uErr == nil {
value.Set(reflect.ValueOf(uid))
return true
}
}
return false
}
func Test_tryAssignUUID(t *testing.T) {
mm := map[string][]string{
"id": []string{"df67786a-a543-4958-87ca-426954c626d5"},
"ids": []string{"df67786a-a543-4958-87ca-426954c626d5", "68dc3815-6ab5-4883-81a1-96eff25b659f"},
}
type UUIDs struct {
Id uuid.UUID `uri:"Id"`
Ids []uuid.UUID `uri:"Ids"`
}
var received UUIDs
if err := mapFormByTag(&received, mm, "uri"); err != nil {
t.Error(err)
}
expected := UUIDs{
Id: uuid.FromStringOrNil("df67786a-a543-4958-87ca-426954c626d5"),
Ids: []uuid.UUID{
uuid.FromStringOrNil("df67786a-a543-4958-87ca-426954c626d5"),
uuid.FromStringOrNil("68dc3815-6ab5-4883-81a1-96eff25b659f"),
},
}
assert.Equal(t, received, expected)
}
To make it work with arrays I added a case
to setWithProperType
like:
case reflect.Array:
if tryAssignUUID(val, value) {
return nil
}
return errUnknownType
And modification for setByForm
:
case reflect.Array:
if !ok {
vs = []string{opt.defaultValue}
}
if len(vs) != value.Len() {
if len(vs) == 1 && tryAssignUUID(vs[0], value) {
return true, nil
}
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
}
return true, setArray(vs, value, field)
Comment From: AnatolyUA
It would be preferable to merge #3045, though.