1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
20 // A Binder translates between string parameters and Go data structures.
22 // Bind takes the name and type of the desired parameter and constructs it
23 // from one or more values from Params.
28 // url?id=123&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=rob
31 // Example.Action(id int, ol []int, ul []string, user User)
34 // Bind(params, "id", int): 123
35 // Bind(params, "ol", []int): {1, 2}
36 // Bind(params, "ul", []string): {"str", "array"}
37 // Bind(params, "user", User): User{Name:"rob"}
39 // Note that only exported struct fields may be bound.
40 Bind func(params *Params, name string, typ reflect.Type) reflect.Value
42 // Unbind serializes a given value to one or more URL parameters of the given
44 Unbind func(output map[string]string, name string, val interface{})
47 var binderLog = RevelLog.New("section", "binder")
49 // ValueBinder is adapter for easily making one-key-value binders.
50 func ValueBinder(f func(value string, typ reflect.Type) reflect.Value) func(*Params, string, reflect.Type) reflect.Value {
51 return func(params *Params, name string, typ reflect.Type) reflect.Value {
52 vals, ok := params.Values[name]
53 if !ok || len(vals) == 0 {
54 return reflect.Zero(typ)
56 return f(vals[0], typ)
60 // Revel's default date and time constants
62 DefaultDateFormat = "2006-01-02"
63 DefaultDateTimeFormat = "2006-01-02 15:04"
66 // Binders type and kind definition
68 // These are the lookups to find a Binder for any type of data.
69 // The most specific binder found will be used (Type before Kind)
70 TypeBinders = make(map[reflect.Type]Binder)
71 KindBinders = make(map[reflect.Kind]Binder)
73 // Applications can add custom time formats to this array, and they will be
74 // automatically attempted when binding a time.Time.
75 TimeFormats = []string{}
82 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
84 return reflect.Zero(typ)
86 intValue, err := strconv.ParseInt(val, 10, 64)
88 binderLog.Warn("IntBinder Conversion Error", "error", err)
89 return reflect.Zero(typ)
91 pValue := reflect.New(typ)
92 pValue.Elem().SetInt(intValue)
95 Unbind: func(output map[string]string, key string, val interface{}) {
96 output[key] = fmt.Sprintf("%d", val)
101 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
103 return reflect.Zero(typ)
105 uintValue, err := strconv.ParseUint(val, 10, 64)
107 binderLog.Warn("UintBinder Conversion Error", "error", err)
108 return reflect.Zero(typ)
110 pValue := reflect.New(typ)
111 pValue.Elem().SetUint(uintValue)
114 Unbind: func(output map[string]string, key string, val interface{}) {
115 output[key] = fmt.Sprintf("%d", val)
119 FloatBinder = Binder{
120 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
122 return reflect.Zero(typ)
124 floatValue, err := strconv.ParseFloat(val, 64)
126 binderLog.Warn("FloatBinder Conversion Error", "error", err)
127 return reflect.Zero(typ)
129 pValue := reflect.New(typ)
130 pValue.Elem().SetFloat(floatValue)
133 Unbind: func(output map[string]string, key string, val interface{}) {
134 output[key] = fmt.Sprintf("%f", val)
138 StringBinder = Binder{
139 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
140 return reflect.ValueOf(val)
142 Unbind: func(output map[string]string, name string, val interface{}) {
143 output[name] = val.(string)
147 // Booleans support a various value formats,
148 // refer `revel.Atob` method.
150 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
151 return reflect.ValueOf(Atob(val))
153 Unbind: func(output map[string]string, name string, val interface{}) {
154 output[name] = fmt.Sprintf("%t", val)
158 PointerBinder = Binder{
159 Bind: func(params *Params, name string, typ reflect.Type) reflect.Value {
160 v := Bind(params, name, typ.Elem())
167 Unbind: func(output map[string]string, name string, val interface{}) {
168 Unbind(output, name, reflect.ValueOf(val).Elem().Interface())
173 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
174 for _, f := range TimeFormats {
175 if r, err := time.ParseInLocation(f, val, TimeZone); err == nil {
176 return reflect.ValueOf(r)
179 return reflect.Zero(typ)
181 Unbind: func(output map[string]string, name string, val interface{}) {
184 format = DateTimeFormat
187 if h == 0 && m == 0 && s == 0 {
190 output[name] = t.Format(format)
200 // Used to keep track of the index for individual keyvalues.
201 type sliceValue struct {
202 index int // Index extracted from brackets. If -1, no index was provided.
203 value reflect.Value // the bound value for this slice element.
206 // This function creates a slice of the given type, Binds each of the individual
207 // elements, and then sets them to their appropriate location in the slice.
208 // If elements are provided without an explicit index, they are added (in
209 // unspecified order) to the end of the slice.
210 func bindSlice(params *Params, name string, typ reflect.Type) reflect.Value {
211 // Collect an array of slice elements with their indexes (and the max index).
214 sliceValues := []sliceValue{}
216 // Factor out the common slice logic (between form values and files).
217 processElement := func(key string, vals []string, files []*multipart.FileHeader) {
218 if !strings.HasPrefix(key, name+"[") {
222 // Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
224 leftBracket, rightBracket := len(name), strings.Index(key[len(name):], "]")+len(name)
225 if rightBracket > leftBracket+1 {
226 index, _ = strconv.Atoi(key[leftBracket+1 : rightBracket])
228 subKeyIndex := rightBracket + 1
230 // Handle the indexed case.
232 if index > maxIndex {
235 sliceValues = append(sliceValues, sliceValue{
237 value: Bind(params, key[:subKeyIndex], typ.Elem()),
242 // It's an un-indexed element. (e.g. element[])
243 numNoIndex += len(vals) + len(files)
244 for _, val := range vals {
245 // Unindexed values can only be direct-bound.
246 sliceValues = append(sliceValues, sliceValue{
248 value: BindValue(val, typ.Elem()),
252 for _, fileHeader := range files {
253 sliceValues = append(sliceValues, sliceValue{
255 value: BindFile(fileHeader, typ.Elem()),
260 for key, vals := range params.Values {
261 processElement(key, vals, nil)
263 for key, fileHeaders := range params.Files {
264 processElement(key, nil, fileHeaders)
267 resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
268 for _, sv := range sliceValues {
270 resultArray.Index(sv.index).Set(sv.value)
272 resultArray = reflect.Append(resultArray, sv.value)
279 // Break on dots and brackets.
280 // e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar"
281 func nextKey(key string) string {
282 fieldLen := strings.IndexAny(key, ".[")
286 return key[:fieldLen]
289 func unbindSlice(output map[string]string, name string, val interface{}) {
290 v := reflect.ValueOf(val)
291 for i := 0; i < v.Len(); i++ {
292 Unbind(output, fmt.Sprintf("%s[%d]", name, i), v.Index(i).Interface())
296 func bindStruct(params *Params, name string, typ reflect.Type) reflect.Value {
297 resultPointer := reflect.New(typ)
298 result := resultPointer.Elem()
299 if params.JSON != nil {
300 // Try to inject the response as a json into the created result
301 if err := json.Unmarshal(params.JSON, resultPointer.Interface()); err != nil {
302 binderLog.Error("bindStruct Unable to unmarshal request", "name", name, "error", err, "data", string(params.JSON))
306 fieldValues := make(map[string]reflect.Value)
307 for key := range params.Values {
308 if !strings.HasPrefix(key, name+".") {
312 // Get the name of the struct property.
313 // Strip off the prefix. e.g. foo.bar.baz => bar.baz
314 suffix := key[len(name)+1:]
315 fieldName := nextKey(suffix)
316 fieldLen := len(fieldName)
318 if _, ok := fieldValues[fieldName]; !ok {
319 // Time to bind this field. Get it and make sure we can set it.
320 fieldValue := result.FieldByName(fieldName)
321 if !fieldValue.IsValid() {
322 binderLog.Warn("bindStruct Field not found", "name", fieldName)
325 if !fieldValue.CanSet() {
326 binderLog.Warn("bindStruct Field not settable", "name", fieldName)
329 boundVal := Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type())
330 fieldValue.Set(boundVal)
331 fieldValues[fieldName] = boundVal
338 func unbindStruct(output map[string]string, name string, iface interface{}) {
339 val := reflect.ValueOf(iface)
341 for i := 0; i < val.NumField(); i++ {
342 structField := typ.Field(i)
343 fieldValue := val.Field(i)
345 // PkgPath is specified to be empty exactly for exported fields.
346 if structField.PkgPath == "" {
347 Unbind(output, fmt.Sprintf("%s.%s", name, structField.Name), fieldValue.Interface())
352 // Helper that returns an upload of the given name, or nil.
353 func getMultipartFile(params *Params, name string) multipart.File {
354 for _, fileHeader := range params.Files[name] {
355 file, err := fileHeader.Open()
359 binderLog.Warn("getMultipartFile: Failed to open uploaded file", "name", name, "error", err)
364 func bindFile(params *Params, name string, typ reflect.Type) reflect.Value {
365 reader := getMultipartFile(params, name)
367 return reflect.Zero(typ)
370 // If it's already stored in a temp file, just return that.
371 if osFile, ok := reader.(*os.File); ok {
372 return reflect.ValueOf(osFile)
375 // Otherwise, have to store it.
376 tmpFile, err := ioutil.TempFile("", "revel-upload")
378 binderLog.Warn("bindFile: Failed to create a temp file to store upload", "name", name, "error", err)
379 return reflect.Zero(typ)
382 // Register it to be deleted after the request is done.
383 params.tmpFiles = append(params.tmpFiles, tmpFile)
385 _, err = io.Copy(tmpFile, reader)
387 binderLog.Warn("bindFile: Failed to copy upload to temp file", "name", name, "error", err)
388 return reflect.Zero(typ)
391 _, err = tmpFile.Seek(0, 0)
393 binderLog.Warn("bindFile: Failed to seek to beginning of temp file", "name", name, "error", err)
394 return reflect.Zero(typ)
397 return reflect.ValueOf(tmpFile)
400 func bindByteArray(params *Params, name string, typ reflect.Type) reflect.Value {
401 if reader := getMultipartFile(params, name); reader != nil {
402 b, err := ioutil.ReadAll(reader)
404 return reflect.ValueOf(b)
406 binderLog.Warn("bindByteArray: Error reading uploaded file contents", "name", name, "error", err)
408 return reflect.Zero(typ)
411 func bindReadSeeker(params *Params, name string, typ reflect.Type) reflect.Value {
412 if reader := getMultipartFile(params, name); reader != nil {
413 return reflect.ValueOf(reader.(io.ReadSeeker))
415 return reflect.Zero(typ)
418 // bindMap converts parameters using map syntax into the corresponding map. e.g.:
419 // params["a[5]"]=foo, name="a", typ=map[int]string => map[int]string{5: "foo"}
420 func bindMap(params *Params, name string, typ reflect.Type) reflect.Value {
423 valueType = typ.Elem()
424 resultPtr = reflect.New(reflect.MapOf(keyType, valueType))
425 result = resultPtr.Elem()
427 result.Set(reflect.MakeMap(typ))
428 if params.JSON != nil {
429 // Try to inject the response as a json into the created result
430 if err := json.Unmarshal(params.JSON, resultPtr.Interface()); err != nil {
431 binderLog.Warn("bindMap: Unable to unmarshal request", "name", name, "error", err)
436 for paramName := range params.Values {
437 // The paramName string must start with the value in the "name" parameter,
438 // otherwise there is no way the parameter is part of the map
439 if !strings.HasPrefix(paramName, name) {
443 suffix := paramName[len(name)+1:]
444 fieldName := nextKey(suffix)
446 fieldName = fieldName[:len(fieldName)-1]
448 if !strings.HasPrefix(paramName, name+"["+fieldName+"]") {
452 result.SetMapIndex(BindValue(fieldName, keyType), Bind(params, name+"["+fieldName+"]", valueType))
457 func unbindMap(output map[string]string, name string, iface interface{}) {
458 mapValue := reflect.ValueOf(iface)
459 for _, key := range mapValue.MapKeys() {
460 Unbind(output, name+"["+fmt.Sprintf("%v", key.Interface())+"]",
461 mapValue.MapIndex(key).Interface())
465 // Bind takes the name and type of the desired parameter and constructs it
466 // from one or more values from Params.
467 // Returns the zero value of the type upon any sort of failure.
468 func Bind(params *Params, name string, typ reflect.Type) reflect.Value {
469 if binder, found := binderForType(typ); found {
470 return binder.Bind(params, name, typ)
472 return reflect.Zero(typ)
475 func BindValue(val string, typ reflect.Type) reflect.Value {
476 return Bind(&Params{Values: map[string][]string{"": {val}}}, "", typ)
479 func BindFile(fileHeader *multipart.FileHeader, typ reflect.Type) reflect.Value {
480 return Bind(&Params{Files: map[string][]*multipart.FileHeader{"": {fileHeader}}}, "", typ)
483 func Unbind(output map[string]string, name string, val interface{}) {
484 if binder, found := binderForType(reflect.TypeOf(val)); found {
485 if binder.Unbind != nil {
486 binder.Unbind(output, name, val)
488 binderLog.Error("Unbind: Unable to unmarshal request", "name", name, "value", val)
493 func binderForType(typ reflect.Type) (Binder, bool) {
494 binder, ok := TypeBinders[typ]
496 binder, ok = KindBinders[typ.Kind()]
498 binderLog.Error("binderForType: no binder for type", "type", typ)
499 return Binder{}, false
505 // Sadly, the binder lookups can not be declared initialized -- that results in
506 // an "initialization loop" compile error.
508 KindBinders[reflect.Int] = IntBinder
509 KindBinders[reflect.Int8] = IntBinder
510 KindBinders[reflect.Int16] = IntBinder
511 KindBinders[reflect.Int32] = IntBinder
512 KindBinders[reflect.Int64] = IntBinder
514 KindBinders[reflect.Uint] = UintBinder
515 KindBinders[reflect.Uint8] = UintBinder
516 KindBinders[reflect.Uint16] = UintBinder
517 KindBinders[reflect.Uint32] = UintBinder
518 KindBinders[reflect.Uint64] = UintBinder
520 KindBinders[reflect.Float32] = FloatBinder
521 KindBinders[reflect.Float64] = FloatBinder
523 KindBinders[reflect.String] = StringBinder
524 KindBinders[reflect.Bool] = BoolBinder
525 KindBinders[reflect.Slice] = Binder{bindSlice, unbindSlice}
526 KindBinders[reflect.Struct] = Binder{bindStruct, unbindStruct}
527 KindBinders[reflect.Ptr] = PointerBinder
528 KindBinders[reflect.Map] = MapBinder
530 TypeBinders[reflect.TypeOf(time.Time{})] = TimeBinder
533 TypeBinders[reflect.TypeOf(&os.File{})] = Binder{bindFile, nil}
534 TypeBinders[reflect.TypeOf([]byte{})] = Binder{bindByteArray, nil}
535 TypeBinders[reflect.TypeOf((*io.Reader)(nil)).Elem()] = Binder{bindReadSeeker, nil}
536 TypeBinders[reflect.TypeOf((*io.ReadSeeker)(nil)).Elem()] = Binder{bindReadSeeker, nil}
539 DateTimeFormat = Config.StringDefault("format.datetime", DefaultDateTimeFormat)
540 DateFormat = Config.StringDefault("format.date", DefaultDateFormat)
541 TimeFormats = append(TimeFormats, DateTimeFormat, DateFormat)