瀏覽代碼

Adds FORM bindings

Manu Mtz-Almeida 11 年之前
父節點
當前提交
d90868e5bd
共有 1 個文件被更改,包括 99 次插入0 次删除
  1. 99 0
      binding/binding.go

+ 99 - 0
binding/binding.go

@@ -6,6 +6,7 @@ import (
 	"errors"
 	"net/http"
 	"reflect"
+	"strconv"
 	"strings"
 )
 
@@ -49,9 +50,107 @@ func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
 }
 
 func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
+	if err := req.ParseForm(); err != nil {
+		return err
+	}
+	if err := mapForm(obj, req.Form); err != nil {
+		return err
+	}
+	return Validate(obj)
+}
+
+func mapForm(ptr interface{}, form map[string][]string) error {
+	typ := reflect.TypeOf(ptr).Elem()
+	formStruct := reflect.ValueOf(ptr).Elem()
+	for i := 0; i < typ.NumField(); i++ {
+		typeField := typ.Field(i)
+		if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
+			structField := formStruct.Field(i)
+			if !structField.CanSet() {
+				continue
+			}
+
+			inputValue, exists := form[inputFieldName]
+			if !exists {
+				continue
+			}
+			numElems := len(inputValue)
+			if structField.Kind() == reflect.Slice && numElems > 0 {
+				sliceOf := structField.Type().Elem().Kind()
+				slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
+				for i := 0; i < numElems; i++ {
+					if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
+						return err
+					}
+				}
+				formStruct.Elem().Field(i).Set(slice)
+			} else {
+				if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
+					return err
+				}
+			}
+		}
+	}
 	return nil
 }
 
+func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
+	switch valueKind {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		if val == "" {
+			val = "0"
+		}
+		intVal, err := strconv.Atoi(val)
+		if err != nil {
+			return err
+		} else {
+			structField.SetInt(int64(intVal))
+		}
+	case reflect.Bool:
+		if val == "" {
+			val = "false"
+		}
+		boolVal, err := strconv.ParseBool(val)
+		if err != nil {
+			return err
+		} else {
+			structField.SetBool(boolVal)
+		}
+	case reflect.Float32:
+		if val == "" {
+			val = "0.0"
+		}
+		floatVal, err := strconv.ParseFloat(val, 32)
+		if err != nil {
+			return err
+		} else {
+			structField.SetFloat(floatVal)
+		}
+	case reflect.Float64:
+		if val == "" {
+			val = "0.0"
+		}
+		floatVal, err := strconv.ParseFloat(val, 64)
+		if err != nil {
+			return err
+		} else {
+			structField.SetFloat(floatVal)
+		}
+	case reflect.String:
+		structField.SetString(val)
+	}
+	return nil
+}
+
+// Don't pass in pointers to bind to. Can lead to bugs. See:
+// https://github.com/codegangsta/martini-contrib/issues/40
+// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
+func ensureNotPointer(obj interface{}) {
+	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
+		panic("Pointers are not accepted as binding models")
+	}
+}
+
 func Validate(obj interface{}) error {
 
 	typ := reflect.TypeOf(obj)