diff --git a/param/methodparams.go b/param/methodparams.go index 2e6e6840..9cd2b1d6 100644 --- a/param/methodparams.go +++ b/param/methodparams.go @@ -5,6 +5,7 @@ import ( "reflect" beecontext "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" ) //Keeps param information to be auto passed to controller methods @@ -13,7 +14,7 @@ type MethodParam struct { parser paramParser location paramLocation required bool - defValue interface{} + defValue string } type paramLocation byte @@ -38,6 +39,18 @@ func Int(name string, opts ...MethodParamOption) *MethodParam { return newParam(name, intParser{}, opts) } +func Float(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, floatParser{}, opts) +} + +func Time(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, timeParser{}, opts) +} + +func Json(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, jsonParser{}, opts) +} + func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { param = &MethodParam{name: name, parser: parser} for _, option := range opts { @@ -46,42 +59,47 @@ func newParam(name string, parser paramParser, opts []MethodParamOption) (param return } -func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { - result = make([]reflect.Value, 0, len(methodParams)) - i := 0 - for _, p := range methodParams { - var strValue string - var value interface{} - switch p.location { - case body: - strValue = string(ctx.Input.RequestBody) - case header: - strValue = ctx.Input.Header(p.name) - default: - strValue = ctx.Input.Query(p.name) +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())) } - if strValue == "" { - if p.required { - ctx.Abort(400, "Missing argument "+p.name) - } else if p.defValue != nil { - value = p.defValue - } else { - value = p.parser.zeroValue() - } - } else { - var err error - value, err = p.parser.parse(strValue) - if err != nil { - //TODO: handle err - } - } - reflectValue, err := safeConvert(reflect.ValueOf(value), methodType.In(i)) + reflectValue, err = safeConvert(reflect.ValueOf(value), paramType) if err != nil { - //TODO: handle err + 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) - i++ } return } diff --git a/param/options.go b/param/options.go index 91d01ac3..6fe10108 100644 --- a/param/options.go +++ b/param/options.go @@ -1,15 +1,25 @@ package param -var InHeader MethodParamOption = func(p *MethodParam) { - p.location = header -} +import ( + "fmt" +) var IsRequired MethodParamOption = func(p *MethodParam) { p.required = true } +var InHeader MethodParamOption = func(p *MethodParam) { + p.location = header +} + +var InBody MethodParamOption = func(p *MethodParam) { + p.location = body +} + func Default(defValue interface{}) MethodParamOption { return func(p *MethodParam) { - p.defValue = defValue + if defValue != nil { + p.defValue = fmt.Sprintf("%v", defValue) + } } } diff --git a/param/parsers.go b/param/parsers.go index eec0bb79..e3032e20 100644 --- a/param/parsers.go +++ b/param/parsers.go @@ -1,41 +1,67 @@ package param -import "strconv" +import ( + "encoding/json" + "reflect" + "strconv" + "time" +) type paramParser interface { - parse(value string) (interface{}, error) - zeroValue() interface{} + parse(value string, toType reflect.Type) (interface{}, error) } type boolParser struct { } -func (p boolParser) parse(value string) (interface{}, error) { +func (p boolParser) parse(value string, toType reflect.Type) (interface{}, error) { return strconv.ParseBool(value) } -func (p boolParser) zeroValue() interface{} { - return false -} - type stringParser struct { } -func (p stringParser) parse(value string) (interface{}, error) { +func (p stringParser) parse(value string, toType reflect.Type) (interface{}, error) { return value, nil } -func (p stringParser) zeroValue() interface{} { - return "" -} - type intParser struct { } -func (p intParser) parse(value string) (interface{}, error) { +func (p intParser) parse(value string, toType reflect.Type) (interface{}, error) { return strconv.Atoi(value) } -func (p intParser) zeroValue() interface{} { - return 0 +type floatParser struct { +} + +func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) { + if toType.Kind() == reflect.Float32 { + return strconv.ParseFloat(value, 32) + } + return strconv.ParseFloat(value, 64) +} + +type timeParser struct { +} + +func (p timeParser) parse(value string, toType reflect.Type) (result interface{}, err error) { + result, err = time.Parse(time.RFC3339, value) + if err != nil { + result, err = time.Parse("2006-01-02", value) + } + return +} + +type jsonParser struct { +} + +func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error) { + pResult := reflect.New(toType) + v := pResult.Interface() + err := json.Unmarshal([]byte(value), v) + if err != nil { + return nil, err + } + return pResult.Elem().Interface(), nil } diff --git a/response/renderer.go b/response/renderer.go index ca5936db..f5f9a52d 100644 --- a/response/renderer.go +++ b/response/renderer.go @@ -25,3 +25,12 @@ func (s StatusCode) Error() string { func (s StatusCode) Render(ctx *beecontext.Context) { ctx.Output.SetStatus(int(s)) } + +type statusCodeWithRender struct { + statusCode int + rendererFunc +} + +func (s statusCodeWithRender) Error() string { + return strconv.Itoa(s.statusCode) +} diff --git a/response/responses.go b/response/responses.go index aa042a35..5fbe4be1 100644 --- a/response/responses.go +++ b/response/responses.go @@ -21,20 +21,26 @@ func Json(value interface{}, encoding ...bool) Renderer { }) } -func Error(err error) Renderer { +func errorRenderer(err error) Renderer { return rendererFunc(func(ctx *beecontext.Context) { ctx.Output.SetStatus(500) ctx.WriteString(err.Error()) }) } +func Redirect(localurl string) statusCodeWithRender { + return statusCodeWithRender{302, func(ctx *beecontext.Context) { + ctx.Redirect(302, localurl) + }} +} + func RenderMethodResult(result interface{}, ctx *beecontext.Context) { if result != nil { renderer, ok := result.(Renderer) if !ok { err, ok := result.(error) if ok { - renderer = Error(err) + renderer = errorRenderer(err) } else { renderer = Json(result) }