Description

I would like to bind a query parameter into a custom type (uuid.UUID). Is there an interface to implement on custom type to support this ?

It works with json binding for example, as custom type implements Unmarshaler interface.

How to reproduce

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/google/uuid"
)

type ID uuid.UUID

type QueryStruct struct {
    MyID ID `form:"id"`
}

func main() {
    g := gin.Default()
    g.GET("/test", func(c *gin.Context) {
        var query QueryStruct
        if err := c.BindQuery(&query); err != nil {
            c.AbortWithError(400, err)
            return
        }
        c.String(200, "OK")
    })
    g.Run(":9000")
}

Expectations

$ curl http://localhost:9000/test?id=46697e30-349e-4d21-b586-a18674e50385
OK

Actual result

$ curl http://localhost:9000/test?id=46697e30-349e-4d21-b586-a18674e50385
["46697e30-349e-4d21-b586-a18674e50385"] is not valid value for main.ID

Environment

  • go version: 1.15.7
  • gin version (or commit ref): 1.6.3
  • operating system: darwin/amd64

Comment From: tprei

I think you can bind it using a binding like: binding="uuid", check full docs here: validator

Comment From: edebernis

Hello,

Thanks for your help, however same error with:

type QueryStruct struct {
    MyID ID `form:"id" binding:"uuid"`
}

Comment From: tprei

In the google/uuid it says that their uuid's are based on RFC 4122, so maybe try binding="uuid_rfc4122". If that doesn't work, try other versions as listed in the docs: validator

Comment From: edebernis

Same error with all versions listed.

Comment From: zadil

Hello, You can do otherwise like this :

uuid := c.Query("id") if !IsValidUUID(uuid) { err := errors.New("A simple message") c.AbortWithError(400, err) return }

` import uuid "github.com/satori/go.uuid"

func IsValidUUID(u string) bool { _, err := uuid.FromString(u) return err == nil }

`

Comment From: edebernis

Hello @zadil,

Thanks for your answer, however, the problem is not on validation. The issue is on binding, which interface can I implement on custom type "ID" to allow binding.

Comment From: radhian-amri

Hi @edebernis Binding it to uuid.UUID is not possible since the datatype is array(type UUID [16]byte) and what you are providing is string. Thats why its getting the error ["46697e30-349e-4d21-b586-a18674e50385"] is not valid value for main.ID (notice it is in array format) What you can do is create a separate field to parse the string value into the new field ```package main

import ( "github.com/gin-gonic/gin" "github.com/google/uuid" )

type QueryStruct struct { MyID string form:"id" json:"-" NewID uuid.UUID json:"id" }

func main() { g := gin.Default() g.GET("/test", func(c *gin.Context) { var query QueryStruct if err := c.BindQuery(&query); err != nil { c.AbortWithError(400, err) return } query.NewID, _ = uuid.Parse(query.MyID) c.JSON(200, query) }) g.Run(":9000") }

let me know if this helps

**Comment From: kszafran**

Related issue: https://github.com/gin-gonic/gin/issues/2673

**Comment From: ssoroka**

I'm having the same problem. What we need is a way to support custom unmarshalling for non-struct types. If you look at the `setWithProperType` function, it only checks for marshalling support for struct types, not for others. This causes problems for all `[]byte`, or say, an int field that is rendered in hex to the client, or a custom timestamp field that stores the underlying value as an int, etc.

a simple solution here might just be to first check to see if the destination type supports the unmarshaller interface. Something like:

```go
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
    if _, ok := value.Interface().(json.Unmarshaler); ok {
        return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
    }
    switch value.Kind() {
    ...

Comment From: kszafran

@ssoroka Two remarks: - I think it should be checking for the encoding.TextUnmarshaler interface for non-struct types - instead of calling json.Unmarshal, you could just call the UnmarshalText (or UnmarshalJSON) function directly

There are some other things to consider, too. Check out this discussion: https://github.com/gin-gonic/gin/issues/2673

Comment From: ssoroka

that's fine with me, and would solve my problem. At the moment there is no way to do this.

Comment From: ssoroka

Here's what I ended up with

func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
    if u, ok := value.Addr().Interface().(encoding.TextUnmarshaler); ok {
        return u.UnmarshalText(bytesconv.StringToBytes(val))
    }

Comment From: eloyekunle

This is still an issue. When will it be fixed in gin?