这是我的申请:
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")
}
所以,如果有人有更好的解决方案,请告诉我。
一个解决方案是将地址定义为地址切片,并将每个地址作为 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")
}