fix: to prevent query , cookie, header,uri tag bind value into request body struct. (#1)

* fix: bug uri, query, header, cookie can bind value into param body struct
This commit is contained in:
tangx 2021-08-06 22:16:10 +08:00 committed by GitHub
parent 14f40ade40
commit 5960b6f5f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 15 deletions

View File

@ -48,7 +48,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
- [Bind Uri](#bind-uri) - [Bind Uri](#bind-uri)
- [Bind Header](#bind-header) - [Bind Header](#bind-header)
- [Bind Cookie](#bind-cookie) - [Bind Cookie](#bind-cookie)
- [Bind Request](#bind-request) - [Bind Request](#bind-request)
- [Bind HTML checkboxes](#bind-html-checkboxes) - [Bind HTML checkboxes](#bind-html-checkboxes)
- [Multipart/Urlencoded binding](#multiparturlencoded-binding) - [Multipart/Urlencoded binding](#multiparturlencoded-binding)
- [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering) - [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering)
@ -1003,10 +1003,14 @@ see `Authorization` in `Params` of [Bind Request](#bind-request)
`c.BindRequest` and `c.ShouldBindRquest` can bind all wanted values into struct instance at once. `c.BindRequest` and `c.ShouldBindRquest` can bind all wanted values into struct instance at once.
Now, Now,
+ supports values from `form`, `uri`, `header`, `cookie` + supports values from `query`, `uri`, `header`, `cookie`, `form`
and request `req.Body`. and `body` for request `req.Body`.
+ `req.Body` decocder is decided by header `Content-Type` value, see more [request_test.go](./binding/binding_request_test.go#L40) + `req.Body` decocder is decided by header `Content-Type` value, see more [request_test.go](./binding/binding_request_test.go#L40)
> ❗️ Attention:
>> 1. tag `query`, `uri`, `header`, `cookie` must never be used in body struct, or it will be panic.
>> 2. tag `query` only work for `c.BindRequest` and `c.ShouldBindRequest` api.
**example** **example**
```go ```go
@ -1019,8 +1023,8 @@ import (
type Params struct { type Params struct {
Name string `uri:"name"` Name string `uri:"name"`
Age int `form:"age,default=18"` Age int `query:"age,default=18"`
Money int32 `form:"money" binding:"required"` Money int32 `query:"money" binding:"required"`
Authorization string `cookie:"Authorization"` Authorization string `cookie:"Authorization"`
UserAgent string `header:"User-Agent"` UserAgent string `header:"User-Agent"`
Data struct { Data struct {

View File

@ -10,8 +10,8 @@ import (
type Params struct { type Params struct {
Name string `uri:"name"` Name string `uri:"name"`
Age int `form:"age,default=18"` Age int `query:"age,default=18"`
Money int32 `form:"money" binding:"required"` Money int32 `query:"money" binding:"required"`
Authorization string `cookie:"Authorization"` Authorization string `cookie:"Authorization"`
UserAgent string `header:"User-Agent"` UserAgent string `header:"User-Agent"`
Data struct { Data struct {

View File

@ -1,10 +1,13 @@
package binding package binding
import ( import (
"errors"
"net/http" "net/http"
"reflect" "reflect"
) )
var ErrInvalidTagInRequestBody = errors.New("body struct should not contain tag `query`, `header`, `cookie`, `uri` in binding request api")
type requestBinding struct{} type requestBinding struct{}
func (requestBinding) Name() string { func (requestBinding) Name() string {
@ -25,12 +28,14 @@ func (b requestBinding) BindOnly(obj interface{}, req *http.Request, uriMap map[
return err return err
} }
binders := []interface{}{Header, Query, Cookie} if err := b.bindingQuery(req, obj); err != nil {
return err
}
binders := []Binding{Header, Cookie}
for _, binder := range binders { for _, binder := range binders {
if b, ok := binder.(Binding); ok { if err := binder.BindOnly(req, obj); err != nil {
if err := b.BindOnly(req, obj); err != nil { return err
return err
}
} }
} }
@ -50,6 +55,11 @@ func (b requestBinding) BindOnly(obj interface{}, req *http.Request, uriMap map[
} }
func (b requestBinding) bindingQuery(req *http.Request, obj interface{}) error {
values := req.URL.Query()
return mapFormByTag(obj, values, "query")
}
// extractBody return body object // extractBody return body object
func extractBody(obj interface{}) interface{} { func extractBody(obj interface{}) interface{} {
@ -60,10 +70,16 @@ func extractBody(obj interface{}) interface{} {
return nil return nil
} }
return extract(rv)
}
func extract(rv reflect.Value) interface{} {
typ := rv.Type() typ := rv.Type()
for i := 0; i < rv.NumField(); i++ { for i := 0; i < rv.NumField(); i++ {
tf := typ.Field(i) tf := typ.Field(i)
vf := rv.Field(i) vf := rv.Field(i)
_, ok := tf.Tag.Lookup("body") _, ok := tf.Tag.Lookup("body")
if !ok { if !ok {
continue continue
@ -71,9 +87,32 @@ func extractBody(obj interface{}) interface{} {
// find body struct // find body struct
if reflect.Indirect(vf).Kind() == reflect.Struct { if reflect.Indirect(vf).Kind() == reflect.Struct {
// body must not has tag "query"
if hasTag(vf, "query") || hasTag(vf, "header") ||
hasTag(vf, "cookie") || hasTag(vf, "uri") {
panic(ErrInvalidTagInRequestBody)
}
return vf.Addr().Interface() return vf.Addr().Interface()
} }
} }
return nil return nil
} }
func hasTag(rv reflect.Value, tag string) bool {
rv = reflect.Indirect(rv)
if rv.Kind() != reflect.Struct {
return false
}
typ := rv.Type()
for i := 0; i < typ.NumField(); i++ {
_, ok := typ.Field(i).Tag.Lookup(tag)
if ok {
return true
}
}
return false
}

View File

@ -724,13 +724,18 @@ func (c *Context) ShouldBindUri(obj interface{}) error {
// `uri`, // `uri`,
// `query` // `query`
// `header` and // `header` and
// `body data` with tag `body:"body"` // `body data` with tag `body:""`
// and it's decoder is decided by header `Content-Type` value // and it's decoder is decided by header `Content-Type` value
// following tags must not be used in body struct, or it will be panic,
// uri
// query
// header
// cookie
// //
// type Params struct { // type Params struct {
// Name string `uri:"name"` // Name string `uri:"name"`
// Age int `form:"age,default=18"` // Age int `query:"age,default=18"`
// Money int32 `form:"money" binding:"required"` // Money int32 `query:"money" binding:"required"`
// Authorization string `cookie:"Authorization"` // Authorization string `cookie:"Authorization"`
// UserAgent string `header:"User-Agent"` // UserAgent string `header:"User-Agent"`
// Data struct { // Data struct {