54b92e95 by astaxie

context:add Bind function

// 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=
astaxie
// var id int  beegoInput.Bind(&id, "id")  id ==123
// var isok bool  beegoInput.Bind(&isok, "isok")  id ==true
// var ft float64  beegoInput.Bind(&ft, "ft")  ft ==1.2
// ol := make([]int, 0, 2)  beegoInput.Bind(&ol, "ol")  ol ==[1 2]
// ul := make([]string, 0, 2)  beegoInput.Bind(&ul, "ul")  ul ==[str
array]
// user struct{Name}  beegoInput.Bind(&user, "user")  user ==
{Name:"astaxie"}
1 parent aa68ffec
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
5 "errors" 5 "errors"
6 "io/ioutil" 6 "io/ioutil"
7 "net/http" 7 "net/http"
8 "net/url"
8 "reflect" 9 "reflect"
9 "strconv" 10 "strconv"
10 "strings" 11 "strings"
...@@ -276,3 +277,244 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { ...@@ -276,3 +277,244 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
276 } 277 }
277 return nil 278 return nil
278 } 279 }
280
281 // Bind data from request.Form[key] to dest
282 // like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
283 // var id int beegoInput.Bind(&id, "id") id ==123
284 // var isok bool beegoInput.Bind(&isok, "isok") id ==true
285 // var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2
286 // ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2]
287 // ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array]
288 // user struct{Name} beegoInput.Bind(&user, "user") user == {Name:"astaxie"}
289 func (input *BeegoInput) Bind(dest interface{}, key string) error {
290 value := reflect.ValueOf(dest)
291 if value.Kind() != reflect.Ptr {
292 return errors.New("beego: non-pointer passed to Bind: " + key)
293 }
294 value = value.Elem()
295 if !value.CanSet() {
296 return errors.New("beego: non-settable variable passed to Bind: " + key)
297 }
298 rv := input.bind(key, value.Type())
299 if !rv.IsValid() {
300 return errors.New("beego: reflect value is empty")
301 }
302 value.Set(rv)
303 return nil
304 }
305
306 func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value {
307 rv := reflect.Zero(reflect.TypeOf(0))
308 switch typ.Kind() {
309 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
310 val := input.Query(key)
311 if len(val) == 0 {
312 return rv
313 }
314 rv = input.bindInt(val, typ)
315 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
316 val := input.Query(key)
317 if len(val) == 0 {
318 return rv
319 }
320 rv = input.bindUint(val, typ)
321 case reflect.Float32, reflect.Float64:
322 val := input.Query(key)
323 if len(val) == 0 {
324 return rv
325 }
326 rv = input.bindFloat(val, typ)
327 case reflect.String:
328 val := input.Query(key)
329 if len(val) == 0 {
330 return rv
331 }
332 rv = input.bindString(val, typ)
333 case reflect.Bool:
334 val := input.Query(key)
335 if len(val) == 0 {
336 return rv
337 }
338 rv = input.bindBool(val, typ)
339 case reflect.Slice:
340 rv = input.bindSlice(&input.Request.Form, key, typ)
341 case reflect.Struct:
342 rv = input.bindStruct(&input.Request.Form, key, typ)
343 case reflect.Ptr:
344 rv = input.bindPoint(key, typ)
345 case reflect.Map:
346 rv = input.bindMap(&input.Request.Form, key, typ)
347 }
348 return rv
349 }
350
351 func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value {
352 rv := reflect.Zero(reflect.TypeOf(0))
353 switch typ.Kind() {
354 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
355 rv = input.bindInt(val, typ)
356 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
357 rv = input.bindUint(val, typ)
358 case reflect.Float32, reflect.Float64:
359 rv = input.bindFloat(val, typ)
360 case reflect.String:
361 rv = input.bindString(val, typ)
362 case reflect.Bool:
363 rv = input.bindBool(val, typ)
364 case reflect.Slice:
365 rv = input.bindSlice(&url.Values{"": {val}}, "", typ)
366 case reflect.Struct:
367 rv = input.bindStruct(&url.Values{"": {val}}, "", typ)
368 case reflect.Ptr:
369 rv = input.bindPoint(val, typ)
370 case reflect.Map:
371 rv = input.bindMap(&url.Values{"": {val}}, "", typ)
372 }
373 return rv
374 }
375
376 func (input *BeegoInput) bindInt(val string, typ reflect.Type) reflect.Value {
377 intValue, err := strconv.ParseInt(val, 10, 64)
378 if err != nil {
379 return reflect.Zero(typ)
380 }
381 pValue := reflect.New(typ)
382 pValue.Elem().SetInt(intValue)
383 return pValue.Elem()
384 }
385
386 func (input *BeegoInput) bindUint(val string, typ reflect.Type) reflect.Value {
387 uintValue, err := strconv.ParseUint(val, 10, 64)
388 if err != nil {
389 return reflect.Zero(typ)
390 }
391 pValue := reflect.New(typ)
392 pValue.Elem().SetUint(uintValue)
393 return pValue.Elem()
394 }
395
396 func (input *BeegoInput) bindFloat(val string, typ reflect.Type) reflect.Value {
397 floatValue, err := strconv.ParseFloat(val, 64)
398 if err != nil {
399 return reflect.Zero(typ)
400 }
401 pValue := reflect.New(typ)
402 pValue.Elem().SetFloat(floatValue)
403 return pValue.Elem()
404 }
405
406 func (input *BeegoInput) bindString(val string, typ reflect.Type) reflect.Value {
407 return reflect.ValueOf(val)
408 }
409
410 func (input *BeegoInput) bindBool(val string, typ reflect.Type) reflect.Value {
411 val = strings.TrimSpace(strings.ToLower(val))
412 switch val {
413 case "true", "on", "1":
414 return reflect.ValueOf(true)
415 }
416 return reflect.ValueOf(false)
417 }
418
419 type sliceValue struct {
420 index int // Index extracted from brackets. If -1, no index was provided.
421 value reflect.Value // the bound value for this slice element.
422 }
423
424 func (input *BeegoInput) bindSlice(params *url.Values, key string, typ reflect.Type) reflect.Value {
425 maxIndex := -1
426 numNoIndex := 0
427 sliceValues := []sliceValue{}
428 for reqKey, vals := range *params {
429 if !strings.HasPrefix(reqKey, key+"[") {
430 continue
431 }
432 // Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
433 index := -1
434 leftBracket, rightBracket := len(key), strings.Index(reqKey[len(key):], "]")+len(key)
435 if rightBracket > leftBracket+1 {
436 index, _ = strconv.Atoi(reqKey[leftBracket+1 : rightBracket])
437 }
438 subKeyIndex := rightBracket + 1
439
440 // Handle the indexed case.
441 if index > -1 {
442 if index > maxIndex {
443 maxIndex = index
444 }
445 sliceValues = append(sliceValues, sliceValue{
446 index: index,
447 value: input.bind(reqKey[:subKeyIndex], typ.Elem()),
448 })
449 continue
450 }
451
452 // It's an un-indexed element. (e.g. element[])
453 numNoIndex += len(vals)
454 for _, val := range vals {
455 // Unindexed values can only be direct-bound.
456 sliceValues = append(sliceValues, sliceValue{
457 index: -1,
458 value: input.bindValue(val, typ.Elem()),
459 })
460 }
461 }
462 resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
463 for _, sv := range sliceValues {
464 if sv.index != -1 {
465 resultArray.Index(sv.index).Set(sv.value)
466 } else {
467 resultArray = reflect.Append(resultArray, sv.value)
468 }
469 }
470 return resultArray
471 }
472
473 func (input *BeegoInput) bindStruct(params *url.Values, key string, typ reflect.Type) reflect.Value {
474 result := reflect.New(typ).Elem()
475 fieldValues := make(map[string]reflect.Value)
476 for reqKey, val := range *params {
477 if !strings.HasPrefix(reqKey, key+".") {
478 continue
479 }
480
481 fieldName := reqKey[len(key)+1:]
482
483 if _, ok := fieldValues[fieldName]; !ok {
484 // Time to bind this field. Get it and make sure we can set it.
485 fieldValue := result.FieldByName(fieldName)
486 if !fieldValue.IsValid() {
487 continue
488 }
489 if !fieldValue.CanSet() {
490 continue
491 }
492 boundVal := input.bindValue(val[0], fieldValue.Type())
493 fieldValue.Set(boundVal)
494 fieldValues[fieldName] = boundVal
495 }
496 }
497
498 return result
499 }
500
501 func (input *BeegoInput) bindPoint(key string, typ reflect.Type) reflect.Value {
502 return input.bind(key, typ.Elem()).Addr()
503 }
504
505 func (input *BeegoInput) bindMap(params *url.Values, key string, typ reflect.Type) reflect.Value {
506 var (
507 result = reflect.MakeMap(typ)
508 keyType = typ.Key()
509 valueType = typ.Elem()
510 )
511 for paramName, values := range *params {
512 if !strings.HasPrefix(paramName, key+"[") || paramName[len(paramName)-1] != ']' {
513 continue
514 }
515
516 key := paramName[len(key)+1 : len(paramName)-1]
517 result.SetMapIndex(input.bindValue(key, keyType), input.bindValue(values[0], valueType))
518 }
519 return result
520 }
......
1 package context
2
3 import (
4 "fmt"
5 "net/http"
6 "testing"
7 )
8
9 func TestParse(t *testing.T) {
10 r, _ := http.NewRequest("GET", "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
11 beegoInput := NewInput(r)
12 beegoInput.ParseFormOrMulitForm(1 << 20)
13
14 var id int
15 err := beegoInput.Bind(&id, "id")
16 if id != 123 || err != nil {
17 t.Fatal("id should has int value")
18 }
19 fmt.Println(id)
20
21 var isok bool
22 err = beegoInput.Bind(&isok, "isok")
23 if !isok || err != nil {
24 t.Fatal("isok should be true")
25 }
26 fmt.Println(isok)
27
28 var float float64
29 err = beegoInput.Bind(&float, "ft")
30 if float != 1.2 || err != nil {
31 t.Fatal("float should be equal to 1.2")
32 }
33 fmt.Println(float)
34
35 ol := make([]int, 0, 2)
36 err = beegoInput.Bind(&ol, "ol")
37 if len(ol) != 2 || err != nil || ol[0] != 1 || ol[1] != 2 {
38 t.Fatal("ol should has two elements")
39 }
40 fmt.Println(ol)
41
42 ul := make([]string, 0, 2)
43 err = beegoInput.Bind(&ul, "ul")
44 if len(ul) != 2 || err != nil || ul[0] != "str" || ul[1] != "array" {
45 t.Fatal("ul should has two elements")
46 }
47 fmt.Println(ul)
48
49 type User struct {
50 Name string
51 }
52 user := User{}
53 err = beegoInput.Bind(&user, "user")
54 if err != nil || user.Name != "astaxie" {
55 t.Fatal("user should has name")
56 }
57 fmt.Println(user)
58 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!