diff --git a/binding/binding.go b/binding/binding.go index 667b3d0f..cb831f30 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -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 } diff --git a/deprecated.go b/deprecated.go new file mode 100644 index 00000000..b6290987 --- /dev/null +++ b/deprecated.go @@ -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) +} diff --git a/gin.go b/gin.go index 2c4d6404..716d2f45 100644 --- a/gin.go +++ b/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) -} - -// 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) +func filterFlags(content string) string { + for i, a := range content { + if a == ' ' || a == ';' { + return content[:i] + } + } + 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 } diff --git a/validation.go b/validation.go deleted file mode 100644 index 501ee502..00000000 --- a/validation.go +++ /dev/null @@ -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 -}