Description

Sorry I'm not familiar with golang, so the description below maybe is not accurate.

Can't bind list of struct pointer in query

It seem like the pointer support was lost in this commit

https://github.com/gin-gonic/gin/commit/0d50ce859745354fa83dcf2bf4c972abed25e53b?diff=split#diff-b0f2c06474cd98fa718e657b7037a516ada93c3de8f51fd795bfdf0ab3e2ade5L133-L138

I try to add these lines into setWithProperType,then the problem is resolved

    case reflect.Ptr:
        if !value.Elem().IsValid() {
            value.Set(reflect.New(value.Type().Elem()))
        }
        return setWithProperType(val, value.Elem(), field)

How to reproduce

package main

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

type Struct struct {
    Id int64 `json:"id"`
}

type Req struct {
    Items []*Struct `json:"items" form:"items"`
}

func main() {
    r := gin.Default()

    r.GET("/api", func(c *gin.Context) {
        req := new(Req)
        err := c.Bind(req)
        if err != nil {
            c.JSON(500, err.Error())
            return
        }
        c.JSON(200, req)
    })

    _ = r.Run("127.0.0.1:8080")
}

Expectations

$ curl -i 'http://localhost:8080/api?items=\{"id":1\}&items=\{"id":2\}'
{"items":[{"id":1},{"id":2}]}

Actual result

$ curl -i 'http://localhost:8080/api?items=\{"id":1\}&items=\{"id":2\}'
"unknown type"

Environment

  • go version: v1.20.4
  • gin version (or commit ref): v1.9.1
  • operating system: macos 13.4.1 arm64

Comment From: omkar-foss

Hey @claneo, I was able to successfully reproduce this issue from your example code snippet above and was able to get the "unknown type" error. My findings as below for your reference.

Root Cause:

As you suggested, the Ptr case in setWithProperType() has been removed in https://github.com/gin-gonic/gin/pull/1749, which is why the default case here gets evaluated in setWithProperType() - leading to the unknown type error.

Solution:

In your example, change []*Struct to []Struct for Items, and then your query request will start working properly without any changes to Gin code. This is because []Struct will be detected as an Array type, which is supported and working well.

I've updated your example as below for reference with comment:

package main

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

type Struct struct {
    Id int64 `json:"id"`
}

type Req struct {
    Items []Struct `json:"items" form:"items"` // Use []Struct instead of []*Struct here
}

func main() {
    r := gin.Default()

    r.GET("/api", func(c *gin.Context) {
        req := new(Req)
        err := c.Bind(req)
        if err != nil {
            c.JSON(500, err.Error())
            return
        }
        c.JSON(200, req)
    })

    _ = r.Run("127.0.0.1:8080")
}

I've tested the above code with Gin v1.9.1 and it works well. Hope it works for you too.

Comment From: claneo

Hi, @omkar-foss , Thanks for your reply.

I know this problem can been fixed by change []*Struct to []Struct. But in my real code, these type is generated from api defination with cli tool. I'm not sure whether this is best practice or not.

So I think maybe gin can support this feature, to cover more use case.

Comment From: omkar-foss

Hey @claneo, indeed, it might be a good idea to keep support for []*Struct to cover relevant use cases. Since there are Go developers who prefer the []*Struct over []Struct in certain cases for performance or other reasons. Although on the other hand, there are articles and developers who strongly suggest that using []*Struct is a bad practice (refer this article for instance).

If we plan on re-adding support for []*Struct, we may need get some background about why the support was removed in https://github.com/gin-gonic/gin/pull/1749, in order to avoid re-introducing any issues caused due to this feature in the past.

@vkd @thinkerou It would be great if you could help by giving us some background about why the Ptr support was removed in https://github.com/gin-gonic/gin/pull/1749. Thanks.

Comment From: vkd

Thanks @omkar-foss for bringing this up.

As far as I remember, there wasn't a test case for such a scenario, and that case block was removed as a redundant. I don't see any potential issues if we implement such a case to process a pointer.

Comment From: omkar-foss

Hey @vkd, thanks a lot for your reply.

I've raised this PR which consists of the re-addition of pointer support (with tests), the code change is an adaptation of the change in this commit where the support was removed as well as suggested by @claneo above.