263 lines
7.3 KiB
Go
263 lines
7.3 KiB
Go
package request
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Bind data from request.Form[key] to dest
|
|
// like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=orange
|
|
// var id int orangeInput.Bind(&id, "id") id ==123
|
|
// var isok bool orangeInput.Bind(&isok, "isok") isok ==true
|
|
// var ft float64 orangeInput.Bind(&ft, "ft") ft ==1.2
|
|
// ol := make([]int, 0, 2) orangeInput.Bind(&ol, "ol") ol ==[1 2]
|
|
// ul := make([]string, 0, 2) orangeInput.Bind(&ul, "ul") ul ==[str array]
|
|
// user struct{Name} orangeInput.Bind(&user, "user") user == {Name:"orange"}
|
|
func (input *OrangeInput) Bind(dest interface{}, key string) error {
|
|
value := reflect.ValueOf(dest)
|
|
if value.Kind() != reflect.Ptr {
|
|
return errors.New("orange: non-pointer passed to Bind: " + key)
|
|
}
|
|
value = value.Elem()
|
|
if !value.CanSet() {
|
|
return errors.New("orange: non-settable variable passed to Bind: " + key)
|
|
}
|
|
typ := value.Type()
|
|
// Get real type if dest define with interface{}.
|
|
// e.g var dest interface{} dest=1.0
|
|
if value.Kind() == reflect.Interface {
|
|
typ = value.Elem().Type()
|
|
}
|
|
rv := input.bind(key, typ)
|
|
if !rv.IsValid() {
|
|
return errors.New("orange: reflect value is empty")
|
|
}
|
|
value.Set(rv)
|
|
return nil
|
|
}
|
|
|
|
func (input *OrangeInput) bind(key string, typ reflect.Type) reflect.Value {
|
|
if input.request.Form == nil {
|
|
input.request.ParseForm()
|
|
}
|
|
rv := reflect.Zero(typ)
|
|
switch typ.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
val := input.Query(key)
|
|
if len(val) == 0 {
|
|
return rv
|
|
}
|
|
rv = input.bindInt(val, typ)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
val := input.Query(key)
|
|
if len(val) == 0 {
|
|
return rv
|
|
}
|
|
rv = input.bindUint(val, typ)
|
|
case reflect.Float32, reflect.Float64:
|
|
val := input.Query(key)
|
|
if len(val) == 0 {
|
|
return rv
|
|
}
|
|
rv = input.bindFloat(val, typ)
|
|
case reflect.String:
|
|
val := input.Query(key)
|
|
if len(val) == 0 {
|
|
return rv
|
|
}
|
|
rv = input.bindString(val, typ)
|
|
case reflect.Bool:
|
|
val := input.Query(key)
|
|
if len(val) == 0 {
|
|
return rv
|
|
}
|
|
rv = input.bindBool(val, typ)
|
|
case reflect.Slice:
|
|
rv = input.bindSlice(&input.request.Form, key, typ)
|
|
case reflect.Struct:
|
|
rv = input.bindStruct(&input.request.Form, key, typ)
|
|
case reflect.Ptr:
|
|
rv = input.bindPoint(key, typ)
|
|
case reflect.Map:
|
|
rv = input.bindMap(&input.request.Form, key, typ)
|
|
}
|
|
return rv
|
|
}
|
|
|
|
func (input *OrangeInput) bindValue(val string, typ reflect.Type) reflect.Value {
|
|
rv := reflect.Zero(typ)
|
|
switch typ.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
rv = input.bindInt(val, typ)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
rv = input.bindUint(val, typ)
|
|
case reflect.Float32, reflect.Float64:
|
|
rv = input.bindFloat(val, typ)
|
|
case reflect.String:
|
|
rv = input.bindString(val, typ)
|
|
case reflect.Bool:
|
|
rv = input.bindBool(val, typ)
|
|
case reflect.Slice:
|
|
rv = input.bindSlice(&url.Values{"": {val}}, "", typ)
|
|
case reflect.Struct:
|
|
rv = input.bindStruct(&url.Values{"": {val}}, "", typ)
|
|
case reflect.Ptr:
|
|
rv = input.bindPoint(val, typ)
|
|
case reflect.Map:
|
|
rv = input.bindMap(&url.Values{"": {val}}, "", typ)
|
|
}
|
|
return rv
|
|
}
|
|
|
|
func (input *OrangeInput) bindInt(val string, typ reflect.Type) reflect.Value {
|
|
intValue, err := strconv.ParseInt(val, 10, 64)
|
|
if err != nil {
|
|
return reflect.Zero(typ)
|
|
}
|
|
pValue := reflect.New(typ)
|
|
pValue.Elem().SetInt(intValue)
|
|
return pValue.Elem()
|
|
}
|
|
|
|
func (input *OrangeInput) bindUint(val string, typ reflect.Type) reflect.Value {
|
|
uintValue, err := strconv.ParseUint(val, 10, 64)
|
|
if err != nil {
|
|
return reflect.Zero(typ)
|
|
}
|
|
pValue := reflect.New(typ)
|
|
pValue.Elem().SetUint(uintValue)
|
|
return pValue.Elem()
|
|
}
|
|
|
|
func (input *OrangeInput) bindFloat(val string, typ reflect.Type) reflect.Value {
|
|
floatValue, err := strconv.ParseFloat(val, 64)
|
|
if err != nil {
|
|
return reflect.Zero(typ)
|
|
}
|
|
pValue := reflect.New(typ)
|
|
pValue.Elem().SetFloat(floatValue)
|
|
return pValue.Elem()
|
|
}
|
|
|
|
func (input *OrangeInput) bindString(val string, typ reflect.Type) reflect.Value {
|
|
return reflect.ValueOf(val)
|
|
}
|
|
|
|
func (input *OrangeInput) bindBool(val string, typ reflect.Type) reflect.Value {
|
|
val = strings.TrimSpace(strings.ToLower(val))
|
|
switch val {
|
|
case "true", "on", "1":
|
|
return reflect.ValueOf(true)
|
|
}
|
|
return reflect.ValueOf(false)
|
|
}
|
|
|
|
type sliceValue struct {
|
|
index int // Index extracted from brackets. If -1, no index was provided.
|
|
value reflect.Value // the bound value for this slice element.
|
|
}
|
|
|
|
func (input *OrangeInput) bindSlice(params *url.Values, key string, typ reflect.Type) reflect.Value {
|
|
maxIndex := -1
|
|
numNoIndex := 0
|
|
sliceValues := []sliceValue{}
|
|
for reqKey, vals := range *params {
|
|
if !strings.HasPrefix(reqKey, key+"[") {
|
|
continue
|
|
}
|
|
// Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
|
|
index := -1
|
|
leftBracket, rightBracket := len(key), strings.Index(reqKey[len(key):], "]")+len(key)
|
|
if rightBracket > leftBracket+1 {
|
|
index, _ = strconv.Atoi(reqKey[leftBracket+1 : rightBracket])
|
|
}
|
|
subKeyIndex := rightBracket + 1
|
|
|
|
// Handle the indexed case.
|
|
if index > -1 {
|
|
if index > maxIndex {
|
|
maxIndex = index
|
|
}
|
|
sliceValues = append(sliceValues, sliceValue{
|
|
index: index,
|
|
value: input.bind(reqKey[:subKeyIndex], typ.Elem()),
|
|
})
|
|
continue
|
|
}
|
|
|
|
// It's an un-indexed element. (e.g. element[])
|
|
numNoIndex += len(vals)
|
|
for _, val := range vals {
|
|
// Unindexed values can only be direct-bound.
|
|
sliceValues = append(sliceValues, sliceValue{
|
|
index: -1,
|
|
value: input.bindValue(val, typ.Elem()),
|
|
})
|
|
}
|
|
}
|
|
resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
|
|
for _, sv := range sliceValues {
|
|
if sv.index != -1 {
|
|
resultArray.Index(sv.index).Set(sv.value)
|
|
} else {
|
|
resultArray = reflect.Append(resultArray, sv.value)
|
|
}
|
|
}
|
|
return resultArray
|
|
}
|
|
|
|
func (input *OrangeInput) bindStruct(params *url.Values, key string, typ reflect.Type) reflect.Value {
|
|
result := reflect.New(typ).Elem()
|
|
fieldValues := make(map[string]reflect.Value)
|
|
for reqKey, val := range *params {
|
|
var fieldName string
|
|
if strings.HasPrefix(reqKey, key+".") {
|
|
fieldName = reqKey[len(key)+1:]
|
|
} else if strings.HasPrefix(reqKey, key+"[") && reqKey[len(reqKey)-1] == ']' {
|
|
fieldName = reqKey[len(key)+1 : len(reqKey)-1]
|
|
} else {
|
|
continue
|
|
}
|
|
|
|
if _, ok := fieldValues[fieldName]; !ok {
|
|
// Time to bind this field. Get it and make sure we can set it.
|
|
fieldValue := result.FieldByName(fieldName)
|
|
if !fieldValue.IsValid() {
|
|
continue
|
|
}
|
|
if !fieldValue.CanSet() {
|
|
continue
|
|
}
|
|
boundVal := input.bindValue(val[0], fieldValue.Type())
|
|
fieldValue.Set(boundVal)
|
|
fieldValues[fieldName] = boundVal
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (input *OrangeInput) bindPoint(key string, typ reflect.Type) reflect.Value {
|
|
return input.bind(key, typ.Elem()).Addr()
|
|
}
|
|
|
|
func (input *OrangeInput) bindMap(params *url.Values, key string, typ reflect.Type) reflect.Value {
|
|
var (
|
|
result = reflect.MakeMap(typ)
|
|
keyType = typ.Key()
|
|
valueType = typ.Elem()
|
|
)
|
|
for paramName, values := range *params {
|
|
if !strings.HasPrefix(paramName, key+"[") || paramName[len(paramName)-1] != ']' {
|
|
continue
|
|
}
|
|
|
|
key := paramName[len(key)+1 : len(paramName)-1]
|
|
result.SetMapIndex(input.bindValue(key, keyType), input.bindValue(values[0], valueType))
|
|
}
|
|
return result
|
|
}
|