使用一个go的集成框架时,遇到参数绑定的问题。大多数时候用的json,通过 POST 方法发送,而在直接使用 gin 时,对于 GET 中的 query 参数,则是直接通过 ShouldBindQuery
这样的方法来绑定。
而在框架中,为了统一解析,是通过读取定义的结构体中的 tag 来判断使用什么方法来解析参数的。通过 ShouldBindWith
方法,传入对应的绑定方法实现绑定。
type Param struct {
Id int `json:"id" query:"id"`
}
仅添加json标签无效后,尝试加上query标签,但依旧没有绑定上参数值。定位到query绑定的方法中来:
// gin-gonic/gin@v1.10.0/binding/query.go
func (queryBinding) Bind(req *http.Request, obj any) error {
values := req.URL.Query()
if err := mapForm(obj, values); err != nil {
return err
}
return validate(obj)
}
这里获取到了查询参数,继续进入 mapForm 方法中:
func mapForm(ptr any, form map[string][]string) error {
return mapFormByTag(ptr, form, "form")
}
原来这里调用 mapFormByTag
进行绑定时,使用的是form
标签,于是加上form标签后,可以成功绑定参数:
type Param struct {
Id int `json:"id" query:"id" form:"id"`
}
那如果只添加 form 标签呢?发现也是可以的。进入 form 方式的绑定方法中:
// gin-gonic/gin@v1.10.0/binding/form.go
func (formBinding) Bind(req *http.Request, obj any) error {
if err := req.ParseForm(); err != nil {
return err
}
if err := req.ParseMultipartForm(defaultMemory); err != nil && !errors.Is(err, http.ErrNotMultipart) {
return err
}
if err := mapForm(obj, req.Form); err != nil {
return err
}
return validate(obj)
}
request 对象中的 ParseForm 方法:
func (r *Request) ParseForm() error {
var err error
if r.PostForm == nil {
if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {
r.PostForm, err = parsePostForm(r)
}
if r.PostForm == nil {
r.PostForm = make(url.Values)
}
}
if r.Form == nil {
if len(r.PostForm) > 0 {
r.Form = make(url.Values)
copyValues(r.Form, r.PostForm)
}
var newValues url.Values
if r.URL != nil {
var e error
// 从url参数解析
newValues, e = url.ParseQuery(r.URL.RawQuery)
if err == nil {
err = e
}
}
if newValues == nil {
newValues = make(url.Values)
}
if r.Form == nil {
r.Form = newValues
} else {
copyValues(r.Form, newValues)
}
}
return err
}
可以看到当表单解析为空时,也会从url的查询参数中解析,因此,也能绑定上。