127b85bc 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 89dde6cd
...@@ -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"
...@@ -261,6 +262,7 @@ func (input *BeegoInput) SetData(key, val interface{}) { ...@@ -261,6 +262,7 @@ func (input *BeegoInput) SetData(key, val interface{}) {
261 input.Data[key] = val 262 input.Data[key] = val
262 } 263 }
263 264
265 // parseForm or parseMultiForm based on Content-type
264 func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { 266 func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
265 // Parse the body depending on the content type. 267 // Parse the body depending on the content type.
266 switch input.Header("Content-Type") { 268 switch input.Header("Content-Type") {
...@@ -278,3 +280,244 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { ...@@ -278,3 +280,244 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
278 280
279 return nil 281 return nil
280 } 282 }
283
284 // Bind data from request.Form[key] to dest
285 // like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
286 // var id int beegoInput.Bind(&id, "id") id ==123
287 // var isok bool beegoInput.Bind(&isok, "isok") id ==true
288 // var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2
289 // ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2]
290 // ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array]
291 // user struct{Name} beegoInput.Bind(&user, "user") user == {Name:"astaxie"}
292 func (input *BeegoInput) Bind(dest interface{}, key string) error {
293 value := reflect.ValueOf(dest)
294 if value.Kind() != reflect.Ptr {
295 return errors.New("beego: non-pointer passed to Bind: " + key)
296 }
297 value = value.Elem()
298 if !value.CanSet() {
299 return errors.New("beego: non-settable variable passed to Bind: " + key)
300 }
301 rv := input.bind(key, value.Type())
302 if !rv.IsValid() {
303 return errors.New("beego: reflect value is empty")
304 }
305 value.Set(rv)
306 return nil
307 }
308
309 func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value {
310 rv := reflect.Zero(reflect.TypeOf(0))
311 switch typ.Kind() {
312 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
313 val := input.Query(key)
314 if len(val) == 0 {
315 return rv
316 }
317 rv = input.bindInt(val, typ)
318 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
319 val := input.Query(key)
320 if len(val) == 0 {
321 return rv
322 }
323 rv = input.bindUint(val, typ)
324 case reflect.Float32, reflect.Float64:
325 val := input.Query(key)
326 if len(val) == 0 {
327 return rv
328 }
329 rv = input.bindFloat(val, typ)
330 case reflect.String:
331 val := input.Query(key)
332 if len(val) == 0 {
333 return rv
334 }
335 rv = input.bindString(val, typ)
336 case reflect.Bool:
337 val := input.Query(key)
338 if len(val) == 0 {
339 return rv
340 }
341 rv = input.bindBool(val, typ)
342 case reflect.Slice:
343 rv = input.bindSlice(&input.Request.Form, key, typ)
344 case reflect.Struct:
345 rv = input.bindStruct(&input.Request.Form, key, typ)
346 case reflect.Ptr:
347 rv = input.bindPoint(key, typ)
348 case reflect.Map:
349 rv = input.bindMap(&input.Request.Form, key, typ)
350 }
351 return rv
352 }
353
354 func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value {
355 rv := reflect.Zero(reflect.TypeOf(0))
356 switch typ.Kind() {
357 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
358 rv = input.bindInt(val, typ)
359 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
360 rv = input.bindUint(val, typ)
361 case reflect.Float32, reflect.Float64:
362 rv = input.bindFloat(val, typ)
363 case reflect.String:
364 rv = input.bindString(val, typ)
365 case reflect.Bool:
366 rv = input.bindBool(val, typ)
367 case reflect.Slice:
368 rv = input.bindSlice(&url.Values{"": {val}}, "", typ)
369 case reflect.Struct:
370 rv = input.bindStruct(&url.Values{"": {val}}, "", typ)
371 case reflect.Ptr:
372 rv = input.bindPoint(val, typ)
373 case reflect.Map:
374 rv = input.bindMap(&url.Values{"": {val}}, "", typ)
375 }
376 return rv
377 }
378
379 func (input *BeegoInput) bindInt(val string, typ reflect.Type) reflect.Value {
380 intValue, err := strconv.ParseInt(val, 10, 64)
381 if err != nil {
382 return reflect.Zero(typ)
383 }
384 pValue := reflect.New(typ)
385 pValue.Elem().SetInt(intValue)
386 return pValue.Elem()
387 }
388
389 func (input *BeegoInput) bindUint(val string, typ reflect.Type) reflect.Value {
390 uintValue, err := strconv.ParseUint(val, 10, 64)
391 if err != nil {
392 return reflect.Zero(typ)
393 }
394 pValue := reflect.New(typ)
395 pValue.Elem().SetUint(uintValue)
396 return pValue.Elem()
397 }
398
399 func (input *BeegoInput) bindFloat(val string, typ reflect.Type) reflect.Value {
400 floatValue, err := strconv.ParseFloat(val, 64)
401 if err != nil {
402 return reflect.Zero(typ)
403 }
404 pValue := reflect.New(typ)
405 pValue.Elem().SetFloat(floatValue)
406 return pValue.Elem()
407 }
408
409 func (input *BeegoInput) bindString(val string, typ reflect.Type) reflect.Value {
410 return reflect.ValueOf(val)
411 }
412
413 func (input *BeegoInput) bindBool(val string, typ reflect.Type) reflect.Value {
414 val = strings.TrimSpace(strings.ToLower(val))
415 switch val {
416 case "true", "on", "1":
417 return reflect.ValueOf(true)
418 }
419 return reflect.ValueOf(false)
420 }
421
422 type sliceValue struct {
423 index int // Index extracted from brackets. If -1, no index was provided.
424 value reflect.Value // the bound value for this slice element.
425 }
426
427 func (input *BeegoInput) bindSlice(params *url.Values, key string, typ reflect.Type) reflect.Value {
428 maxIndex := -1
429 numNoIndex := 0
430 sliceValues := []sliceValue{}
431 for reqKey, vals := range *params {
432 if !strings.HasPrefix(reqKey, key+"[") {
433 continue
434 }
435 // Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
436 index := -1
437 leftBracket, rightBracket := len(key), strings.Index(reqKey[len(key):], "]")+len(key)
438 if rightBracket > leftBracket+1 {
439 index, _ = strconv.Atoi(reqKey[leftBracket+1 : rightBracket])
440 }
441 subKeyIndex := rightBracket + 1
442
443 // Handle the indexed case.
444 if index > -1 {
445 if index > maxIndex {
446 maxIndex = index
447 }
448 sliceValues = append(sliceValues, sliceValue{
449 index: index,
450 value: input.bind(reqKey[:subKeyIndex], typ.Elem()),
451 })
452 continue
453 }
454
455 // It's an un-indexed element. (e.g. element[])
456 numNoIndex += len(vals)
457 for _, val := range vals {
458 // Unindexed values can only be direct-bound.
459 sliceValues = append(sliceValues, sliceValue{
460 index: -1,
461 value: input.bindValue(val, typ.Elem()),
462 })
463 }
464 }
465 resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
466 for _, sv := range sliceValues {
467 if sv.index != -1 {
468 resultArray.Index(sv.index).Set(sv.value)
469 } else {
470 resultArray = reflect.Append(resultArray, sv.value)
471 }
472 }
473 return resultArray
474 }
475
476 func (input *BeegoInput) bindStruct(params *url.Values, key string, typ reflect.Type) reflect.Value {
477 result := reflect.New(typ).Elem()
478 fieldValues := make(map[string]reflect.Value)
479 for reqKey, val := range *params {
480 if !strings.HasPrefix(reqKey, key+".") {
481 continue
482 }
483
484 fieldName := reqKey[len(key)+1:]
485
486 if _, ok := fieldValues[fieldName]; !ok {
487 // Time to bind this field. Get it and make sure we can set it.
488 fieldValue := result.FieldByName(fieldName)
489 if !fieldValue.IsValid() {
490 continue
491 }
492 if !fieldValue.CanSet() {
493 continue
494 }
495 boundVal := input.bindValue(val[0], fieldValue.Type())
496 fieldValue.Set(boundVal)
497 fieldValues[fieldName] = boundVal
498 }
499 }
500
501 return result
502 }
503
504 func (input *BeegoInput) bindPoint(key string, typ reflect.Type) reflect.Value {
505 return input.bind(key, typ.Elem()).Addr()
506 }
507
508 func (input *BeegoInput) bindMap(params *url.Values, key string, typ reflect.Type) reflect.Value {
509 var (
510 result = reflect.MakeMap(typ)
511 keyType = typ.Key()
512 valueType = typ.Elem()
513 )
514 for paramName, values := range *params {
515 if !strings.HasPrefix(paramName, key+"[") || paramName[len(paramName)-1] != ']' {
516 continue
517 }
518
519 key := paramName[len(key)+1 : len(paramName)-1]
520 result.SetMapIndex(input.bindValue(key, keyType), input.bindValue(values[0], valueType))
521 }
522 return result
523 }
......
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!