feat(json): json support param and query binding

This commit is contained in:
rol 2022-08-26 19:28:09 +08:00
parent 1c48977cca
commit c65a6b8f25
13 changed files with 66 additions and 13 deletions

View File

@ -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,

View File

@ -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
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

15
binding/option.go Normal file
View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -94,6 +94,10 @@ func EnableJsonDecoderDisallowUnknownFields() {
binding.EnableDecoderDisallowUnknownFields = true
}
func EnableParamsAndQueryBinding() {
binding.EnableParamsAndQueryBinding = true
}
// Mode returns current gin mode.
func Mode() string {
return modeName