Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 583a348

Browse filesBrowse files
committed
Creates binding subpackage for request param data binding. TODO resolve dependency
1 parent bb1d80e commit 583a348
Copy full SHA for 583a348

20 files changed

+700
-511
lines changed

‎binder.go

Copy file name to clipboardExpand all lines: binder.go
-508Lines changed: 0 additions & 508 deletions
This file was deleted.

‎binding/binder.go

Copy file name to clipboard
+70Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package binding
2+
3+
import (
4+
"reflect"
5+
"strings"
6+
)
7+
8+
// A Binder translates between string parameters from a URL and Go data structures.
9+
type Binder struct {
10+
// Bind takes the name and type of the desired parameter and constructs it
11+
// from one or more values from Params.
12+
//
13+
// Example
14+
//
15+
// Request:
16+
// url?id=123&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=rob
17+
//
18+
// Action:
19+
// Example.Action(id int, ol []int, ul []string, user User)
20+
//
21+
// Calls:
22+
// Bind(params, "id", int): 123
23+
// Bind(params, "ol", []int): {1, 2}
24+
// Bind(params, "ul", []string): {"str", "array"}
25+
// Bind(params, "user", User): User{Name:"rob"}
26+
//
27+
// Note that only exported struct fields may be bound.
28+
Bind func(params *Params, name string, typ reflect.Type) reflect.Value
29+
30+
// Unbind serializes a given value to one or more URL parameters of the given
31+
// name.
32+
Unbind func(output map[string]string, name string, val interface{})
33+
}
34+
35+
36+
var (
37+
// These are the lookups to find a Binder for any type of data.
38+
// The most specific binder found will be used (Type before Kind)
39+
TypeBinders = make(map[reflect.Type]Binder)
40+
KindBinders = make(map[reflect.Kind]Binder)
41+
42+
// Applications can add custom time formats to this array, and they will be
43+
// automatically attempted when binding a time.Time.
44+
TimeFormats = []string{}
45+
DateFormat string
46+
DateTimeFormat string
47+
)
48+
49+
50+
// An adapter for easily making one-key-value binders.
51+
func valueBinder(f func(value string, typ reflect.Type) reflect.Value) func(*Params, string, reflect.Type) reflect.Value {
52+
return func(params *Params, name string, typ reflect.Type) reflect.Value {
53+
vals, ok := params.Values[name]
54+
if !ok || len(vals) == 0 {
55+
return reflect.Zero(typ)
56+
}
57+
return f(vals[0], typ)
58+
}
59+
}
60+
61+
62+
// Break on dots and brackets.
63+
// e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar"
64+
func nextKey(key string) string {
65+
fieldLen := strings.IndexAny(key, ".[")
66+
if fieldLen == -1 {
67+
return key
68+
}
69+
return key[:fieldLen]
70+
}

‎binder_test.go renamed to ‎binding/binder_test.go

Copy file name to clipboardExpand all lines: binding/binder_test.go
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package revel
1+
package binding
22

33
import (
44
"fmt"

‎binding/binding.go

Copy file name to clipboard
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// binding is responsible for taking HTTP parmters and binding them to variables for specific Go types
2+
package binding
3+
4+
import (
5+
"mime/multipart"
6+
"reflect"
7+
)
8+
9+
10+
// Bind takes the name and type of the desired parameter and constructs it
11+
// from one or more values from Params.
12+
// Returns the zero value of the type upon any sort of failure.
13+
func Bind(params *map[string][]string, name string, typ reflect.Type) reflect.Value {
14+
if binder, found := binderForType(typ); found {
15+
return binder.Bind(params, name, typ)
16+
}
17+
return reflect.Zero(typ)
18+
}
19+
20+
21+
func BindValue(val string, typ reflect.Type) reflect.Value {
22+
return Bind(map[string][]string{"": {val}}, "", typ)
23+
}
24+
25+
26+
func BindFile(fileHeader *multipart.FileHeader, typ reflect.Type) reflect.Value {
27+
return Bind(&Params{Files: map[string][]*multipart.FileHeader{"": {fileHeader}}}, "", typ)
28+
}
29+
30+
31+
func Unbind(output map[string]string, name string, val interface{}) {
32+
if binder, found := binderForType(reflect.TypeOf(val)); found {
33+
if binder.Unbind != nil {
34+
binder.Unbind(output, name, val)
35+
} else {
36+
// TODO ERROR.Printf("revel/binder: can not unbind %s=%s", name, val)
37+
}
38+
}
39+
}
40+
41+
42+
func binderForType(typ reflect.Type) (Binder, bool) {
43+
binder, ok := TypeBinders[typ]
44+
if !ok {
45+
binder, ok = KindBinders[typ.Kind()]
46+
if !ok {
47+
// TODO WARN.Println("revel/binder: no binder for type:", typ)
48+
return Binder{}, false
49+
}
50+
}
51+
return binder, true
52+
}

‎binding/bool_binder.go

Copy file name to clipboard
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package binding
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
var (
8+
// Booleans support a couple different value formats:
9+
// "true" and "false"
10+
// "on" and "" (a checkbox)
11+
// "1" and "0" (why not)
12+
BoolBinder = Binder{
13+
Bind: valueBinder(func(val string, typ reflect.Type) reflect.Value {
14+
v := strings.TrimSpace(strings.ToLower(val))
15+
switch v {
16+
case "true", "on", "1":
17+
return reflect.ValueOf(true)
18+
}
19+
// Return false by default.
20+
return reflect.ValueOf(false)
21+
}),
22+
Unbind: func(output map[string]string, name string, val interface{}) {
23+
output[name] = fmt.Sprintf("%t", val)
24+
},
25+
}
26+
)
27+
28+
func init() {
29+
KindBinders[reflect.Bool] = boolBinder
30+
}

‎binding/bytearray_binder.go

Copy file name to clipboard
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package binding
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
var (
8+
byteArrayBinder = Binder{bindByteArray, nil}
9+
)
10+
11+
12+
func bindByteArray(params *Params, name string, typ reflect.Type) reflect.Value {
13+
if reader := getMultipartFile(params, name); reader != nil {
14+
b, err := ioutil.ReadAll(reader)
15+
if err == nil {
16+
return reflect.ValueOf(b)
17+
}
18+
// TODO WARN.Println("Error reading uploaded file contents:", err)
19+
}
20+
return reflect.Zero(typ)
21+
}
22+
23+
24+
func init() {
25+
TypeBinders[reflect.TypeOf([]byte{})] = byteArrayBinder
26+
}
27+

‎binding/file_binder.go

Copy file name to clipboard
+65Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package binding
2+
3+
import (
4+
"mime/multipart"
5+
"reflect"
6+
)
7+
8+
var (
9+
fileBinder = Binder{bindFile, nil}
10+
)
11+
12+
13+
func bindFile(params *Params, name string, typ reflect.Type) reflect.Value {
14+
reader := getMultipartFile(params, name)
15+
if reader == nil {
16+
return reflect.Zero(typ)
17+
}
18+
19+
// If it's already stored in a temp file, just return that.
20+
if osFile, ok := reader.(*os.File); ok {
21+
return reflect.ValueOf(osFile)
22+
}
23+
24+
// Otherwise, have to store it.
25+
tmpFile, err := ioutil.TempFile("", "revel-upload")
26+
if err != nil {
27+
// TODO WARN.Println("Failed to create a temp file to store upload:", err)
28+
return reflect.Zero(typ)
29+
}
30+
31+
// Register it to be deleted after the request is done.
32+
params.tmpFiles = append(params.tmpFiles, tmpFile)
33+
34+
_, err = io.Copy(tmpFile, reader)
35+
if err != nil {
36+
// TODO WARN.Println("Failed to copy upload to temp file:", err)
37+
return reflect.Zero(typ)
38+
}
39+
40+
_, err = tmpFile.Seek(0, 0)
41+
if err != nil {
42+
// TODO WARN.Println("Failed to seek to beginning of temp file:", err)
43+
return reflect.Zero(typ)
44+
}
45+
46+
return reflect.ValueOf(tmpFile)
47+
}
48+
49+
50+
// Helper that returns an upload of the given name, or nil.
51+
func getMultipartFile(params *Params, name string) multipart.File {
52+
for _, fileHeader := range params.Files[name] {
53+
file, err := fileHeader.Open()
54+
if err == nil {
55+
return file
56+
}
57+
// TODO WARN.Println("Failed to open uploaded file", name, ":", err)
58+
}
59+
return nil
60+
}
61+
62+
63+
func init() {
64+
TypeBinders[reflect.TypeOf(&os.File{})] = fileBinder
65+
}

‎binding/float_binder.go

Copy file name to clipboard
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package binding
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
var (
8+
floatBinder = Binder{
9+
Bind: valueBinder(func(val string, typ reflect.Type) reflect.Value {
10+
if len(val) == 0 {
11+
return reflect.Zero(typ)
12+
}
13+
floatValue, err := strconv.ParseFloat(val, 64)
14+
if err != nil {
15+
// TODO WARN.Println(err)
16+
return reflect.Zero(typ)
17+
}
18+
pValue := reflect.New(typ)
19+
pValue.Elem().SetFloat(floatValue)
20+
return pValue.Elem()
21+
}),
22+
Unbind: func(output map[string]string, key string, val interface{}) {
23+
output[key] = fmt.Sprintf("%f", val)
24+
},
25+
}
26+
)
27+
28+
func init() {
29+
KindBinders[reflect.Float32] = floatBinder
30+
KindBinders[reflect.Float64] = floatBinder
31+
}

‎binding/int_binder.go

Copy file name to clipboard
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package binding
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
var (
8+
intBinder = Binder{
9+
Bind: valueBinder(func(val string, typ reflect.Type) reflect.Value {
10+
if len(val) == 0 {
11+
return reflect.Zero(typ)
12+
}
13+
intValue, err := strconv.ParseInt(val, 10, 64)
14+
if err != nil {
15+
// TODO WARN.Println(err)
16+
return reflect.Zero(typ)
17+
}
18+
pValue := reflect.New(typ)
19+
pValue.Elem().SetInt(intValue)
20+
return pValue.Elem()
21+
}),
22+
Unbind: func(output map[string]string, key string, val interface{}) {
23+
output[key] = fmt.Sprintf("%d", val)
24+
},
25+
}
26+
)
27+
28+
func init() {
29+
KindBinders[reflect.Int] = intBinder
30+
KindBinders[reflect.Int8] = intBinder
31+
KindBinders[reflect.Int16] = intBinder
32+
KindBinders[reflect.Int32] = intBinder
33+
KindBinders[reflect.Int64] = intBinder
34+
}

‎binding/map_binder.go

Copy file name to clipboard
+48Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package binding
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
var (
8+
mapBinder = Binder{
9+
Bind: bindMap,
10+
Unbind: unbindMap,
11+
}
12+
)
13+
14+
15+
// bindMap converts parameters using map syntax into the corresponding map. e.g.:
16+
// params["a[5]"]=foo, name="a", typ=map[int]string => map[int]string{5: "foo"}
17+
func bindMap(params *Params, name string, typ reflect.Type) reflect.Value {
18+
var (
19+
result = reflect.MakeMap(typ)
20+
keyType = typ.Key()
21+
valueType = typ.Elem()
22+
)
23+
for paramName, values := range params.Values {
24+
if !strings.HasPrefix(paramName, name+"[") || paramName[len(paramName)-1] != ']' {
25+
continue
26+
}
27+
28+
key := paramName[len(name)+1 : len(paramName)-1]
29+
result.SetMapIndex(BindValue(key, keyType), BindValue(values[0], valueType))
30+
}
31+
return result
32+
}
33+
34+
35+
36+
37+
38+
func unbindMap(output map[string]string, name string, iface interface{}) {
39+
mapValue := reflect.ValueOf(iface)
40+
for _, key := range mapValue.MapKeys() {
41+
Unbind(output, name+"["+fmt.Sprintf("%v", key.Interface())+"]",
42+
mapValue.MapIndex(key).Interface())
43+
}
44+
}
45+
46+
func init() {
47+
KindBinders[reflect.Map] = mapBinder
48+
}

‎binding/pointer_binder.go

Copy file name to clipboard
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package binding
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
var (
8+
pointerBinder = Binder{
9+
Bind: func(params *Params, name string, typ reflect.Type) reflect.Value {
10+
return Bind(params, name, typ.Elem()).Addr()
11+
},
12+
Unbind: func(output map[string]string, name string, val interface{}) {
13+
Unbind(output, name, reflect.ValueOf(val).Elem().Interface())
14+
},
15+
}
16+
)
17+
18+
func init() {
19+
KindBinders[reflect.Ptr] = pointerBinder
20+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.