New bindings for JSON, XML and form parsing and validation
This commit is contained in:
parent
1aa3216303
commit
9634a38704
@ -3,32 +3,89 @@ package binding
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"errors"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
Binding interface {
|
||||
Bind(io.Reader, interface{}) error
|
||||
Bind(*http.Request, interface{}) error
|
||||
}
|
||||
|
||||
// JSON binding
|
||||
jsonBinding struct{}
|
||||
|
||||
// JSON binding
|
||||
// XML binding
|
||||
xmlBinding struct{}
|
||||
|
||||
// // form binding
|
||||
formBinding struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
JSON = jsonBinding{}
|
||||
XML = xmlBinding{}
|
||||
Form = formBinding{} // todo
|
||||
)
|
||||
|
||||
func (_ jsonBinding) Bind(r io.Reader, obj interface{}) error {
|
||||
decoder := json.NewDecoder(r)
|
||||
return decoder.Decode(&obj)
|
||||
func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
if err := decoder.Decode(obj); err == nil {
|
||||
return Validate(obj)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (_ xmlBinding) Bind(r io.Reader, obj interface{}) error {
|
||||
decoder := xml.NewDecoder(r)
|
||||
return decoder.Decode(&obj)
|
||||
func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
decoder := xml.NewDecoder(req.Body)
|
||||
if err := decoder.Decode(obj); err == nil {
|
||||
return Validate(obj)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Validate(obj interface{}) error {
|
||||
|
||||
typ := reflect.TypeOf(obj)
|
||||
val := reflect.ValueOf(obj)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
fieldValue := val.Field(i).Interface()
|
||||
zero := reflect.Zero(field.Type).Interface()
|
||||
|
||||
// Validate nested and embedded structs (if pointer, only do so if not nil)
|
||||
if field.Type.Kind() == reflect.Struct ||
|
||||
(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {
|
||||
if err := Validate(fieldValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Index(field.Tag.Get("binding"), "required") > -1 {
|
||||
if reflect.DeepEqual(zero, fieldValue) {
|
||||
name := field.Name
|
||||
if j := field.Tag.Get("json"); j != "" {
|
||||
name = j
|
||||
} else if f := field.Tag.Get("form"); f != "" {
|
||||
name = f
|
||||
}
|
||||
return errors.New("Required " + name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
17
deprecated.go
Normal file
17
deprecated.go
Normal file
@ -0,0 +1,17 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
// DEPRECATED, use Bind() instead.
|
||||
// Like ParseBody() but this method also writes a 400 error if the json is not valid.
|
||||
func (c *Context) EnsureBody(item interface{}) bool {
|
||||
return c.Bind(item)
|
||||
}
|
||||
|
||||
// DEPRECATED use bindings directly
|
||||
// Parses the body content as a JSON input. It decodes the json payload into the struct specified as a pointer.
|
||||
func (c *Context) ParseBody(item interface{}) error {
|
||||
return binding.JSON.Bind(c.Req, item)
|
||||
}
|
47
gin.go
47
gin.go
@ -17,6 +17,11 @@ import (
|
||||
|
||||
const (
|
||||
AbortIndex = math.MaxInt8 / 2
|
||||
MIMEJSON = "application/json"
|
||||
MIMEHTML = "text/html"
|
||||
MIMEXML = "application/xml"
|
||||
MIMEXML2 = "text/xml"
|
||||
MIMEPlain = "text/plain"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -371,16 +376,13 @@ func (c *Context) Get(key string) interface{} {
|
||||
/******** ENCOGING MANAGEMENT********/
|
||||
/************************************/
|
||||
|
||||
// Like ParseBody() but this method also writes a 400 error if the json is not valid.
|
||||
// DEPRECATED, use Bind() instead.
|
||||
func (c *Context) EnsureBody(item interface{}) bool {
|
||||
return c.Bind(item)
|
||||
func filterFlags(content string) string {
|
||||
for i, a := range content {
|
||||
if a == ' ' || a == ';' {
|
||||
return content[:i]
|
||||
}
|
||||
|
||||
// DEPRECATED use bindings directly
|
||||
// Parses the body content as a JSON input. It decodes the json payload into the struct specified as a pointer.
|
||||
func (c *Context) ParseBody(item interface{}) error {
|
||||
return binding.JSON.Bind(c.Req.Body, item)
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// This function checks the Content-Type to select a binding engine automatically,
|
||||
@ -390,27 +392,24 @@ func (c *Context) ParseBody(item interface{}) error {
|
||||
// else --> returns an error
|
||||
// if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
|
||||
func (c *Context) Bind(obj interface{}) bool {
|
||||
var err error
|
||||
switch c.Req.Header.Get("Content-Type") {
|
||||
case "application/json":
|
||||
err = binding.JSON.Bind(c.Req.Body, obj)
|
||||
case "application/xml":
|
||||
err = binding.XML.Bind(c.Req.Body, obj)
|
||||
var b binding.Binding
|
||||
ctype := filterFlags(c.Req.Header.Get("Content-Type"))
|
||||
switch {
|
||||
case c.Req.Method == "GET":
|
||||
b = binding.Form
|
||||
case ctype == MIMEJSON:
|
||||
b = binding.JSON
|
||||
case ctype == MIMEXML || ctype == MIMEXML2:
|
||||
b = binding.XML
|
||||
default:
|
||||
err = errors.New("unknown content-type: " + c.Req.Header.Get("Content-Type"))
|
||||
}
|
||||
if err == nil {
|
||||
err = Validate(c, obj)
|
||||
}
|
||||
if err != nil {
|
||||
c.Fail(400, err)
|
||||
c.Fail(400, errors.New("unknown content-type: "+ctype))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return c.BindWith(obj, b)
|
||||
}
|
||||
|
||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) bool {
|
||||
if err := b.Bind(c.Req.Body, obj); err != nil {
|
||||
if err := b.Bind(c.Req, obj); err != nil {
|
||||
c.Fail(400, err)
|
||||
return false
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Validate(c *Context, obj interface{}) error {
|
||||
|
||||
var err error
|
||||
typ := reflect.TypeOf(obj)
|
||||
val := reflect.ValueOf(obj)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
fieldValue := val.Field(i).Interface()
|
||||
zero := reflect.Zero(field.Type).Interface()
|
||||
|
||||
// Validate nested and embedded structs (if pointer, only do so if not nil)
|
||||
if field.Type.Kind() == reflect.Struct ||
|
||||
(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {
|
||||
err = Validate(c, fieldValue)
|
||||
}
|
||||
|
||||
if strings.Index(field.Tag.Get("binding"), "required") > -1 {
|
||||
if reflect.DeepEqual(zero, fieldValue) {
|
||||
name := field.Name
|
||||
if j := field.Tag.Get("json"); j != "" {
|
||||
name = j
|
||||
} else if f := field.Tag.Get("form"); f != "" {
|
||||
name = f
|
||||
}
|
||||
err = errors.New("Required " + name)
|
||||
c.Error(err, "json validation")
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
Loading…
Reference in New Issue
Block a user