diff --git a/binding/binding.go b/binding/binding.go index a58924ed..0c768d46 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -30,7 +30,7 @@ const ( // the form POST. type Binding interface { Name() string - Bind(*http.Request, any) error + Bind(*http.Request, any, ...Option) error } // BindingBody adds BindBody method to Binding. BindBody is similar with Bind, diff --git a/binding/form.go b/binding/form.go index b17352ba..13815330 100644 --- a/binding/form.go +++ b/binding/form.go @@ -19,7 +19,7 @@ func (formBinding) Name() string { return "form" } -func (formBinding) Bind(req *http.Request, obj any) error { +func (formBinding) Bind(req *http.Request, obj any, opts ...Option) error { if err := req.ParseForm(); err != nil { return err } @@ -36,7 +36,7 @@ func (formPostBinding) Name() string { return "form-urlencoded" } -func (formPostBinding) Bind(req *http.Request, obj any) error { +func (formPostBinding) Bind(req *http.Request, obj any, opts ...Option) error { if err := req.ParseForm(); err != nil { return err } @@ -50,7 +50,7 @@ func (formMultipartBinding) Name() string { return "multipart/form-data" } -func (formMultipartBinding) Bind(req *http.Request, obj any) error { +func (formMultipartBinding) Bind(req *http.Request, obj any, opts ...Option) error { if err := req.ParseMultipartForm(defaultMemory); err != nil { return err } diff --git a/binding/header.go b/binding/header.go index 03bc78da..9c2d5e31 100644 --- a/binding/header.go +++ b/binding/header.go @@ -16,7 +16,7 @@ func (headerBinding) Name() string { return "header" } -func (headerBinding) Bind(req *http.Request, obj any) error { +func (headerBinding) Bind(req *http.Request, obj any, opts ...Option) error { if err := mapHeader(obj, req.Header); err != nil { return err diff --git a/binding/json.go b/binding/json.go index 36eb27a3..2c854e92 100644 --- a/binding/json.go +++ b/binding/json.go @@ -24,16 +24,37 @@ var EnableDecoderUseNumber = false // keys which do not match any non-ignored, exported fields in the destination. var EnableDecoderDisallowUnknownFields = false +var EnableParamsAndQueryBinding = false + type jsonBinding struct{} func (jsonBinding) Name() string { return "json" } -func (jsonBinding) Bind(req *http.Request, obj any) error { +func (jsonBinding) Bind(req *http.Request, obj any, opts ...Option) error { if req == nil || req.Body == nil { return errors.New("invalid request") } + + if EnableParamsAndQueryBinding { + o := &options{} + for _, opt := range opts { + if err := opt(o); nil != err { + return err + } + } + if params := o.params; len(params) > 0 { + b := new(bytes.Buffer) + if err := json.NewEncoder(b).Encode(params); nil != err { + return err + } + if err := decodeJSON(b, obj); err != nil { + return err + } + } + } + return decodeJSON(req.Body, obj) } diff --git a/binding/msgpack.go b/binding/msgpack.go index d1f035e4..a2573a98 100644 --- a/binding/msgpack.go +++ b/binding/msgpack.go @@ -21,7 +21,7 @@ func (msgpackBinding) Name() string { return "msgpack" } -func (msgpackBinding) Bind(req *http.Request, obj any) error { +func (msgpackBinding) Bind(req *http.Request, obj any, opts ...Option) error { return decodeMsgPack(req.Body, obj) } diff --git a/binding/option.go b/binding/option.go new file mode 100644 index 00000000..df59d26d --- /dev/null +++ b/binding/option.go @@ -0,0 +1,15 @@ +package binding + +type options struct { + params map[string]interface{} + query map[string]interface{} +} + +type Option func(opt *options) error + +func WithParams(params map[string]interface{}) Option { + return func(opt *options) error { + opt.params = params + return nil + } +} diff --git a/binding/protobuf.go b/binding/protobuf.go index 44f2fdb9..9bf811de 100644 --- a/binding/protobuf.go +++ b/binding/protobuf.go @@ -18,7 +18,7 @@ func (protobufBinding) Name() string { return "protobuf" } -func (b protobufBinding) Bind(req *http.Request, obj any) error { +func (b protobufBinding) Bind(req *http.Request, obj any, opts ...Option) error { buf, err := ioutil.ReadAll(req.Body) if err != nil { return err diff --git a/binding/query.go b/binding/query.go index c958b88b..ae408761 100644 --- a/binding/query.go +++ b/binding/query.go @@ -12,7 +12,7 @@ func (queryBinding) Name() string { return "query" } -func (queryBinding) Bind(req *http.Request, obj any) error { +func (queryBinding) Bind(req *http.Request, obj any, opts ...Option) error { values := req.URL.Query() if err := mapForm(obj, values); err != nil { return err diff --git a/binding/toml.go b/binding/toml.go index a6b8a90a..eaa791b5 100644 --- a/binding/toml.go +++ b/binding/toml.go @@ -26,7 +26,7 @@ func decodeToml(r io.Reader, obj any) error { return decoder.Decode(obj) } -func (tomlBinding) Bind(req *http.Request, obj any) error { +func (tomlBinding) Bind(req *http.Request, obj any, opts ...Option) error { return decodeToml(req.Body, obj) } diff --git a/binding/xml.go b/binding/xml.go index a70f4ad3..61db63ad 100644 --- a/binding/xml.go +++ b/binding/xml.go @@ -17,7 +17,7 @@ func (xmlBinding) Name() string { return "xml" } -func (xmlBinding) Bind(req *http.Request, obj any) error { +func (xmlBinding) Bind(req *http.Request, obj any, opts ...Option) error { return decodeXML(req.Body, obj) } diff --git a/binding/yaml.go b/binding/yaml.go index b0d36a35..8d0821e6 100644 --- a/binding/yaml.go +++ b/binding/yaml.go @@ -18,7 +18,7 @@ func (yamlBinding) Name() string { return "yaml" } -func (yamlBinding) Bind(req *http.Request, obj any) error { +func (yamlBinding) Bind(req *http.Request, obj any, opts ...Option) error { return decodeYAML(req.Body, obj) } diff --git a/context.go b/context.go index f9489a77..4fdd751f 100644 --- a/context.go +++ b/context.go @@ -732,7 +732,20 @@ func (c *Context) ShouldBindUri(obj any) error { // ShouldBindWith binds the passed struct pointer using the specified binding engine. // See the binding package. func (c *Context) ShouldBindWith(obj any, b binding.Binding) error { - return b.Bind(c.Request, obj) + return b.Bind(c.Request, obj, binding.WithParams(func() (ret map[string]interface{}) { + ret = make(map[string]interface{}) + for _, item := range c.Params { + ret[item.Key] = item.Value + } + for key, val := range c.Request.URL.Query() { + if len(val) == 1 { + ret[key] = val[0] + continue + } + ret[key] = val + } + return + }())) } // ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request diff --git a/mode.go b/mode.go index fd26d907..1822ec53 100644 --- a/mode.go +++ b/mode.go @@ -94,6 +94,10 @@ func EnableJsonDecoderDisallowUnknownFields() { binding.EnableDecoderDisallowUnknownFields = true } +func EnableParamsAndQueryBinding() { + binding.EnableParamsAndQueryBinding = true +} + // Mode returns current gin mode. func Mode() string { return modeName