如何解析 Go Gin 多部分请求中的结构切片

问题描述 投票:0回答:1

这是我的申请:

func main() {
    router := gin.Default()
    router.POST("user", func(c *gin.Context) {
        type address struct {
            City    string `form:"city"`
            Country string `form:"country"`
        }
        type user struct {
            Name      string    `form:"name"`
            Addresses []address `form:"addresses"`
        }

        var payload user
        err := c.Bind(&payload)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{})
            return
        }

        file, err := c.FormFile("image")
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{})
            return
        }

        fmt.Printf("TODO: save user image %s\n", file.Filename)
        fmt.Printf("TODO: save user %s with addresses %d\n", payload.Name, len(payload.Addresses))
    })
    router.Run(":8080")
}

这是我的要求:

curl --location 'http://localhost:8080/user' \
--form 'name="John Smith"' \
--form 'addresses[0][city]="London"' \
--form 'addresses[0][country]="United Kingdom"' \
--form 'image=@"/Users/me/Documents/app/fixtures/user.jpeg"'

问题是地址为零。我无法找到如何使用嵌入切片数据正确反序列化多部分请求的示例。我做错了什么?


更新:

我从 Postman 导出了curl 命令。显然,如果您有“--form”参数,则 POST 方法是隐式的。这是我的应用程序的日志:

TODO: save user image user.jpeg
TODO: save user John Smith with addresses 0

更新2:

事实证明,这样发送数组是错误的:

--表格'地址[0][城市]=“伦敦”'

gin 默认支持的是发送多个同名字段,然后将其转为数组:

--形式'地址=... --form '地址=...

因此,我找到了一个有点古怪的解决方案,将每个地址作为查询参数传递,然后单独解析它们:

要求:

curl --location 'http://localhost:8080/user' \
--form 'name="John Smith"' \
--form 'addresses="?city=London&country=UK"' \
--form 'addresses="?city=Berlin&country=Germany"'
--form 'image=@"/Users/foo/bar/baz/user.jpeg"' \

应用程序:

func main() {
    router := gin.Default()
    router.POST("user", func(c *gin.Context) {
        type address struct {
            City    string `form:"city"`
            Country string `form:"country"`
        }
        type user struct {
            Name      string   `form:"name"`
            Addresses []string `form:"addresses"` // collection_format:"multi"
        }

        var payload user
        err := c.Bind(&payload)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{})
            return
        }

        var addresses []address
        for _, path := range payload.Addresses {
            parsed, err := url.Parse(path)
            if err != nil {
                continue
            }
            query := parsed.Query()
            addresses = append(addresses, address{
                City:    query.Get("city"),
                Country: query.Get("country"),
            })
        }

        file, err := c.FormFile("image")
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{})
            return
        }

        fmt.Printf("TODO: save user image %s\n", file.Filename)
        fmt.Printf("TODO: save user %s with addresses %d\n", payload.Name, len(addresses))

    })
    router.Run(":8080")
}

所以,如果有人有更好的解决方案,请告诉我。

go slice multipart go-gin
1个回答
0
投票

一个解决方案是将地址定义为地址切片,并将每个地址作为 json 而不是查询参数传递,而不是将地址定义为字符串切片。

请求-

curl --location 'http://localhost:8080/getForm' \
--form 'name ="Zargam"' \
--form 'addresses="{\"city\":\"Delhi\",\"country\":\"India\"}"' \
--form 'addresses="{\"city\":\"Shanghai\",\"country\":\"China\"}"'

代码-

package main

import (
    "encoding/json"
    "net/http"

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

type address struct {
    City    string `form:"city"`
    Country string `form:"country"`
}
type user struct {
    Name      string    `form:"name"`
    Addresses []address `form:"addresses"` // collection_format:"multi"
}

func processForm(c *gin.Context) {
    var payload user
    err := c.Bind(&payload)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{})
        return
    }
    payloadByte, _ := json.Marshal(payload)

    c.JSON(http.StatusOK, gin.H{"message": "success", "data": string(payloadByte)})
    return
}

func main() {
    r := gin.Default()
    r.POST("/getForm", processForm)
    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
© www.soinside.com 2019 - 2024. All rights reserved.