From 19f4a6ac0b51cc685fc966b7e8ca12fd89e5bc43 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Sun, 23 Apr 2017 21:37:09 +0300 Subject: [PATCH] slice support --- param/conv.go | 68 ++++++++++++++++++++++++++++++++++++++++ param/methodparams.go | 72 +++---------------------------------------- param/parsers.go | 29 ++++++++++++++++- router.go | 2 +- 4 files changed, 102 insertions(+), 69 deletions(-) create mode 100644 param/conv.go diff --git a/param/conv.go b/param/conv.go new file mode 100644 index 00000000..e909c4ea --- /dev/null +++ b/param/conv.go @@ -0,0 +1,68 @@ +package param + +import ( + "fmt" + "reflect" + + beecontext "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" +) + +func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { + var strValue string + var reflectValue reflect.Value + switch param.location { + case body: + strValue = string(ctx.Input.RequestBody) + case header: + strValue = ctx.Input.Header(param.name) + default: + strValue = ctx.Input.Query(param.name) + } + + if strValue == "" { + if param.required { + ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) + } else { + strValue = param.defValue + } + } + if strValue == "" { + reflectValue = reflect.Zero(paramType) + } else { + value, err := param.parser.parse(strValue, paramType) + if err != nil { + logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %s, Error: %s", param.name, paramType, strValue, err)) + ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %s to type %s", param.name, strValue, paramType)) + } + + reflectValue, err = safeConvert(reflect.ValueOf(value), paramType) + if err != nil { + panic(err) + } + } + return reflectValue +} + +func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { + result = make([]reflect.Value, 0, len(methodParams)) + for i := 0; i < len(methodParams); i++ { + reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) + result = append(result, reflectValue) + } + return +} + +func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("%v", r) + } + } + }() + result = value.Convert(t) + return +} diff --git a/param/methodparams.go b/param/methodparams.go index 9cd2b1d6..a3e25a55 100644 --- a/param/methodparams.go +++ b/param/methodparams.go @@ -1,13 +1,5 @@ package param -import ( - "fmt" - "reflect" - - beecontext "github.com/astaxie/beego/context" - "github.com/astaxie/beego/logs" -) - //Keeps param information to be auto passed to controller methods type MethodParam struct { name string @@ -51,6 +43,11 @@ func Json(name string, opts ...MethodParamOption) *MethodParam { return newParam(name, jsonParser{}, opts) } +func AsSlice(param *MethodParam) *MethodParam { + param.parser = sliceParser(param.parser) + return param +} + func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { param = &MethodParam{name: name, parser: parser} for _, option := range opts { @@ -58,62 +55,3 @@ func newParam(name string, parser paramParser, opts []MethodParamOption) (param } return } - -func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { - var strValue string - var reflectValue reflect.Value - switch param.location { - case body: - strValue = string(ctx.Input.RequestBody) - case header: - strValue = ctx.Input.Header(param.name) - default: - strValue = ctx.Input.Query(param.name) - } - - if strValue == "" { - if param.required { - ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) - } else { - strValue = param.defValue - } - } - if strValue == "" { - reflectValue = reflect.Zero(paramType) - } else { - value, err := param.parser.parse(strValue, paramType) - if err != nil { - logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %s, Parser: %s, Error: %s", param.name, paramType.Name(), strValue, reflect.TypeOf(param.parser).Name(), err)) - ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %s to type %s", param.name, strValue, paramType.Name())) - } - - reflectValue, err = safeConvert(reflect.ValueOf(value), paramType) - if err != nil { - panic(err) - } - } - return reflectValue -} - -func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { - result = make([]reflect.Value, 0, len(methodParams)) - for i := 0; i < len(methodParams); i++ { - reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) - result = append(result, reflectValue) - } - return -} - -func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { - defer func() { - if r := recover(); r != nil { - var ok bool - err, ok = r.(error) - if !ok { - err = fmt.Errorf("%v", r) - } - } - }() - result = value.Convert(t) - return -} diff --git a/param/parsers.go b/param/parsers.go index e3032e20..cfa1a981 100644 --- a/param/parsers.go +++ b/param/parsers.go @@ -4,6 +4,7 @@ import ( "encoding/json" "reflect" "strconv" + "strings" "time" ) @@ -11,6 +12,12 @@ type paramParser interface { parse(value string, toType reflect.Type) (interface{}, error) } +type parserFunc func(value string, toType reflect.Type) (interface{}, error) + +func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error) { + return f(value, toType) +} + type boolParser struct { } @@ -37,7 +44,11 @@ type floatParser struct { func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) { if toType.Kind() == reflect.Float32 { - return strconv.ParseFloat(value, 32) + res, err := strconv.ParseFloat(value, 32) + if err != nil { + return nil, err + } + return float32(res), nil } return strconv.ParseFloat(value, 64) } @@ -65,3 +76,19 @@ func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error } return pResult.Elem().Interface(), nil } + +func sliceParser(elemParser paramParser) paramParser { + return parserFunc(func(value string, toType reflect.Type) (interface{}, error) { + values := strings.Split(value, ",") + result := reflect.MakeSlice(toType, 0, len(values)) + elemType := toType.Elem() + for _, v := range values { + parsedValue, err := elemParser.parse(v, elemType) + if err != nil { + return nil, err + } + result = reflect.Append(result, reflect.ValueOf(parsedValue)) + } + return result.Interface(), nil + }) +} diff --git a/router.go b/router.go index 4937354a..be69419c 100644 --- a/router.go +++ b/router.go @@ -905,7 +905,7 @@ func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, ex //looping in reverse order for the case when both error and value are returned and error sets the response status code for i := len(results) - 1; i >= 0; i-- { result := results[i] - if !result.IsNil() { + if result.Kind() != reflect.Interface || !result.IsNil() { resultValue := result.Interface() response.RenderMethodResult(resultValue, context) }