Closed xmx closed 2 years ago
这是 Go 匿名嵌套 struct
用法错误。
type Student struct {
person // 这个结构体必须是首字母小写
School string `json:"school" form:"school"`
Grade int `json:"grade" form:"grade"`
}
_这种用法,属于匿名嵌套 _ 描述有误,不删除,作为记录。struct
,对于匿名嵌套,内层struct
仍是 外层 struct
的一个 Field
,既然是 Field
,那么它就有一个名字,该名字就是 内层 struct
类型的名字;对于上面的代码而言,就是 person
。根据 Go语言规范,只有名字的首字母是大写的才能对外可见;对于上面的代码而言,名为 person
的 Field
是对外不可见的,因此,Bind
方法无法访问它,也就无法更新它的值,Bind
对其也就不成功(不是 Bind
有问题,而是 Go 语言规范或编译器所限制的)。
解决这个问题有以下几个方法:
person
类型的名字改为 Person
type Person struct {
Name string `json:"name" form:"name"`
Age int `json:"age" form:"age"`
}
type Student struct {
Person `json:"person"`
School string `json:"school" form:"school"`
Grade int `json:"grade" form:"grade"`
}
此时,匿名结构体 Field 的名字就变成 Person
了,因此,可以 Bind
它的值了。
如果结构体的类型名必须是小写字母开头,可以明确写出 Field 的名字,以代替匿名结构体,如下:
type person struct {
Name string `json:"name" form:"name"`
Age int `json:"age" form:"age"`
}
type Student struct {
Person persion `json:"person"`
School string `json:"school" form:"school"`
Grade int `json:"grade" form:"grade"`
}
其它说明: 如果既要内嵌结构体的类型名以小写字母开头,又要必须使用匿名结构体方式,那么这个匿名结构体 Field 就只能在当前包中才能访问,对于 Bind
功能,就要自己手动实现。
上面的解释不周,对于 匿名嵌套 struct 的名字可见性 的描述部分有误,正确的是:当匿名 struct 自身的 Field
对外是可见时,不管匿名 struct 的类型自身是否是对外可见的,其对外可见的 Field
会自动变相地成为外层 struct 的对外可见的 Field
。
对于 Form
类型的 Bind
,稍后改进,支持上述场景。
@xmx 已经修复。更新 版本到 v5.1.1
即可。如下:
// go.mod
module mypackage
require github.com/xgfone/ship/v5 v5.1.1
go 1.11
// main.go
package main
import (
"fmt"
"net/http"
"github.com/xgfone/ship/v5"
)
type person struct {
Name string `json:"name" form:"name"`
Age int `json:"age" form:"age"`
}
type Student struct {
person // 这个结构体必须是首字母小写
School string `json:"school" form:"school"`
Grade int `json:"grade" form:"grade"`
}
func Demo(c *ship.Context) error {
var stu Student
if err := c.Bind(&stu); err != nil {
return ship.ErrBadRequest.New(err)
}
fmt.Println(stu.Name) // OK
fmt.Println(stu.Age) // OK
fmt.Println(stu.School) // OK
fmt.Println(stu.Grade) // OK
return c.JSON(http.StatusOK, stu)
}
func main() {
router := ship.Default()
router.Route("/demo").POST(Demo)
ship.StartServer(":9999", router)
// $ go run main.go
//
// 请求 body 格式是 FormData:
// $ curl -X POST -F "name=小明" -F "age=9" -F "school=新民小学" -F "grade=3" http://localhost:9999/demo
//
// 响应结果:
// {"name":"小明","age":9,"school":"新民小学","grade":3}
}
速度!
go version:1.17.3 ship version:5.1.0
复现代码: