diff --git a/README.md b/README.md index 6b84c3bf..0dd41b99 100644 --- a/README.md +++ b/README.md @@ -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 Header](#bind-header) - [Bind Cookie](#bind-cookie) - - [Bind Request](#bind-request) + - [Bind Request](#bind-request) - [Bind HTML checkboxes](#bind-html-checkboxes) - [Multipart/Urlencoded binding](#multiparturlencoded-binding) - [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. Now, -+ supports values from `form`, `uri`, `header`, `cookie` -and request `req.Body`. ++ supports values from `query`, `uri`, `header`, `cookie`, `form` +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) +> ❗️ 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** ```go @@ -1019,8 +1023,8 @@ import ( type Params struct { Name string `uri:"name"` - Age int `form:"age,default=18"` - Money int32 `form:"money" binding:"required"` + Age int `query:"age,default=18"` + Money int32 `query:"money" binding:"required"` Authorization string `cookie:"Authorization"` UserAgent string `header:"User-Agent"` Data struct { diff --git a/binding/binding_request_test.go b/binding/binding_request_test.go index b569cb45..347fd86a 100644 --- a/binding/binding_request_test.go +++ b/binding/binding_request_test.go @@ -10,8 +10,8 @@ import ( type Params struct { Name string `uri:"name"` - Age int `form:"age,default=18"` - Money int32 `form:"money" binding:"required"` + Age int `query:"age,default=18"` + Money int32 `query:"money" binding:"required"` Authorization string `cookie:"Authorization"` UserAgent string `header:"User-Agent"` Data struct { diff --git a/binding/request.go b/binding/request.go index 60163a68..bcbd2f7d 100644 --- a/binding/request.go +++ b/binding/request.go @@ -1,10 +1,13 @@ package binding import ( + "errors" "net/http" "reflect" ) +var ErrInvalidTagInRequestBody = errors.New("body struct should not contain tag `query`, `header`, `cookie`, `uri` in binding request api") + type requestBinding struct{} func (requestBinding) Name() string { @@ -25,12 +28,14 @@ func (b requestBinding) BindOnly(obj interface{}, req *http.Request, uriMap map[ 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 { - if b, ok := binder.(Binding); ok { - if err := b.BindOnly(req, obj); err != nil { - return err - } + if err := binder.BindOnly(req, obj); err != nil { + 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 func extractBody(obj interface{}) interface{} { @@ -60,10 +70,16 @@ func extractBody(obj interface{}) interface{} { return nil } + return extract(rv) +} + +func extract(rv reflect.Value) interface{} { + typ := rv.Type() for i := 0; i < rv.NumField(); i++ { tf := typ.Field(i) vf := rv.Field(i) + _, ok := tf.Tag.Lookup("body") if !ok { continue @@ -71,9 +87,32 @@ func extractBody(obj interface{}) interface{} { // find body 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 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 +} diff --git a/context.go b/context.go index ebfe17bb..e68fd7b0 100644 --- a/context.go +++ b/context.go @@ -724,13 +724,18 @@ func (c *Context) ShouldBindUri(obj interface{}) error { // `uri`, // `query` // `header` and -// `body data` with tag `body:"body"` +// `body data` with tag `body:""` // 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 { // Name string `uri:"name"` -// Age int `form:"age,default=18"` -// Money int32 `form:"money" binding:"required"` +// Age int `query:"age,default=18"` +// Money int32 `query:"money" binding:"required"` // Authorization string `cookie:"Authorization"` // UserAgent string `header:"User-Agent"` // Data struct {