Description

I'm trying to bind a UUID header field. It did not bind successfully but reported an error.

How to reproduce

package main

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

)
func main(){
    type A struct {
        ID uuid.UUID `header:"ID"`
    }

    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        var a A
        err := c.ShouldBindHeader(&a)
        if err != nil {
            panic(err)
        }
        c.JSON(200, gin.H{
            "message": a.ID,
        })
    })
    r.Run()
}

Expectations

requests.get("http://127.0.0.1:8088/ping", headers={"ID":"1b524196-100a-4ff6-8881-bf0e1e509b1d"}) -> 200

Actual result

2022/01/25 23:00:03 [Recovery] 2022/01/25 - 23:00:03 panic recovered:
GET /ping HTTP/1.1
Host: 127.0.0.1:8088
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Id: 1b524196-100a-4ff6-8881-bf0e1e509b1d
User-Agent: python-requests/2.27.1


["1b524196-100a-4ff6-8881-bf0e1e509b1d"] is not valid value for uuid.UUID

Environment

  • go version: go1.17.5
  • gin version (or commit ref): v1.7.7
  • operating system: windows/amd64

Comment From: Bisstocuz

It's not Gin's bug.

type A struct {
    ID uuid.UUID `header:"ID"`
}

should be

type A struct {
    ID string `header:"ID"`
}

If you wanna transfer it to uuid.UUID, use uuid.Parse(a.ID), check this: https://pkg.go.dev/github.com/google/uuid#Parse

Comment From: lynkas

@Bisstocuz Thank you. The UUID in body and URI can be bind correctly. Have "uuid in header cannot be bind directly" been mentioned somewhere?

Comment From: Bisstocuz

github.com/google/uuid is not a standard library, and uuid.UUID is not a compatible type with HTTP headers. It's ordinary to throw an error.

Here is binding code:

    case reflect.Array:
        if !ok {
            vs = []string{opt.defaultValue}
        }
        if len(vs) != value.Len() {
            return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
        }
        return true, setArray(vs, value, field)

Comment From: iyear

If you expect to receive a UUID, you can use binding which is the gin built-in validator.

https://github.com/go-playground/validator#format

You can also choose different UUID types to bind.

func main() {
    type A struct {
        ID string `header:"ID" binding:"required,uuid"`
    }

    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        var a A
        err := c.ShouldBindHeader(&a)
        if err != nil {
            panic(err)
        }
        c.JSON(200, gin.H{
            "message": a.ID,
        })
    })
    r.Run()
}