提问人:Rafał Mańka 提问时间:10/3/2023 最后编辑:Rafał Mańka 更新时间:10/6/2023 访问量:112
如何在 Go Gin 多部分请求中解析结构片
How to parse slice of structs in Go Gin multipart request
问:
这是我的应用程序:
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:
事实证明,像这样发送数组是错误的:
--form 'addresses[0][city]=“伦敦”'
gin 默认支持发送多个同名字段,然后这些字段将转换为数组:
--form 'addresses=... --form 'addresses=...
因此,这导致我找到了一个有点笨拙的解决方案,即将每个地址作为查询参数传递,然后单独解析它们:
请求:
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")
}
所以,如果有人有更好的解决方案,请告诉我。
答:
1赞
Zargham Ahmed
10/6/2023
#1
一种解决方案是,不要将地址定义为字符串切片,而是将其定义为地址切片,并将每个地址作为 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")
}
评论
0赞
Rafał Mańka
10/7/2023
这个解决方案非常有效
评论