1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-15 01:01:03 +00:00

all types working + controller comments generation

This commit is contained in:
eyalpost
2017-04-25 16:00:49 +03:00
parent 3b29a9c12a
commit 9b79437778
6 changed files with 317 additions and 88 deletions

View File

@ -9,39 +9,51 @@ import (
)
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 == "" {
paramValue := getParamValue(param, ctx)
if paramValue == "" {
if param.required {
ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name))
} else {
strValue = param.defValue
paramValue = param.defValue
}
}
if strValue == "" {
reflectValue = reflect.Zero(paramType)
reflectValue, err := parseValue(paramValue, paramType)
if err != nil {
logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %v, Error: %s", param.name, paramType, paramValue, err))
ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %v to type %s", param.name, paramValue, paramType))
}
return reflectValue
}
func getParamValue(param *MethodParam, ctx *beecontext.Context) string {
switch param.location {
case body:
return string(ctx.Input.RequestBody)
case header:
return ctx.Input.Header(param.name)
// if strValue == "" && strings.Contains(param.name, "_") { //magically handle X-Headers?
// strValue = ctx.Input.Header(strings.Replace(param.name, "_", "-", -1))
// }
case path:
return ctx.Input.Query(":" + param.name)
default:
return ctx.Input.Query(param.name)
}
}
func parseValue(paramValue string, paramType reflect.Type) (result reflect.Value, err error) {
if paramValue == "" {
return reflect.Zero(paramType), nil
} else {
value, err := param.parser.parse(strValue, paramType)
value, err := parse(paramValue, 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))
return result, err
}
reflectValue, err = safeConvert(reflect.ValueOf(value), paramType)
if err != nil {
panic(err)
}
return safeConvert(reflect.ValueOf(value), paramType)
}
return reflectValue
}
func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) {

View File

@ -1,9 +1,13 @@
package param
import (
"fmt"
"strings"
)
//Keeps param information to be auto passed to controller methods
type MethodParam struct {
name string
parser paramParser
location paramLocation
required bool
defValue string
@ -13,45 +17,51 @@ type paramLocation byte
const (
param paramLocation = iota
path
body
header
)
type MethodParamOption func(*MethodParam)
func Bool(name string, opts ...MethodParamOption) *MethodParam {
return newParam(name, boolParser{}, opts)
}
func String(name string, opts ...MethodParamOption) *MethodParam {
return newParam(name, stringParser{}, opts)
}
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 AsSlice(param *MethodParam) *MethodParam {
param.parser = sliceParser(param.parser)
return param
func New(name string, opts ...MethodParamOption) *MethodParam {
return newParam(name, nil, opts)
}
func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) {
param = &MethodParam{name: name, parser: parser}
param = &MethodParam{name: name}
for _, option := range opts {
option(param)
}
return
}
func Make(list ...*MethodParam) []*MethodParam {
if len(list) > 0 {
return list
}
return nil
}
func (mp *MethodParam) String() string {
options := []string{}
result := "param.New(\"" + mp.name + "\""
if mp.required {
options = append(options, "param.IsRequired")
}
switch mp.location {
case path:
options = append(options, "param.InPath")
case body:
options = append(options, "param.InBody")
case header:
options = append(options, "param.InHeader")
}
if mp.defValue != "" {
options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defValue))
}
if len(options) > 0 {
result += ", "
}
result += strings.Join(options, ", ")
result += ")"
return result
}

View File

@ -4,6 +4,8 @@ import (
"fmt"
)
type MethodParamOption func(*MethodParam)
var IsRequired MethodParamOption = func(p *MethodParam) {
p.required = true
}
@ -12,6 +14,10 @@ var InHeader MethodParamOption = func(p *MethodParam) {
p.location = header
}
var InPath MethodParamOption = func(p *MethodParam) {
p.location = path
}
var InBody MethodParamOption = func(p *MethodParam) {
p.location = body
}

View File

@ -12,6 +12,45 @@ type paramParser interface {
parse(value string, toType reflect.Type) (interface{}, error)
}
func parse(value string, t reflect.Type) (interface{}, error) {
parser := getParser(t)
return parser.parse(value, t)
}
func getParser(t reflect.Type) paramParser {
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return intParser{}
case reflect.Slice:
if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string
return stringParser{}
}
elemParser := getParser(t.Elem())
if elemParser == (jsonParser{}) {
return elemParser
}
return sliceParser(elemParser)
case reflect.Bool:
return boolParser{}
case reflect.String:
return stringParser{}
case reflect.Float32, reflect.Float64:
return floatParser{}
case reflect.Ptr:
elemParser := getParser(t.Elem())
if elemParser == (jsonParser{}) {
return elemParser
}
return ptrParser(elemParser)
default:
if t.PkgPath() == "time" && t.Name() == "Time" {
return timeParser{}
}
return jsonParser{}
}
}
type parserFunc func(value string, toType reflect.Type) (interface{}, error)
func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error) {
@ -92,3 +131,21 @@ func sliceParser(elemParser paramParser) paramParser {
return result.Interface(), nil
})
}
func ptrParser(elemParser paramParser) paramParser {
return parserFunc(func(value string, toType reflect.Type) (interface{}, error) {
parsedValue, err := elemParser.parse(value, toType.Elem())
if err != nil {
return nil, err
}
newValPtr := reflect.New(toType.Elem())
newVal := reflect.Indirect(newValPtr)
convertedVal, err := safeConvert(reflect.ValueOf(parsedValue), toType.Elem())
if err != nil {
return nil, err
}
newVal.Set(convertedVal)
return newValPtr.Interface(), nil
})
}