1
0
mirror of https://github.com/astaxie/beego.git synced 2025-06-11 19:50:39 +00:00

move under context

This commit is contained in:
eyalpost
2017-04-25 18:39:42 +03:00
parent 9b79437778
commit cbd831042a
9 changed files with 5 additions and 5 deletions

80
context/param/conv.go Normal file
View File

@ -0,0 +1,80 @@
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) {
paramValue := getParamValue(param, ctx)
if paramValue == "" {
if param.required {
ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name))
} else {
paramValue = param.defValue
}
}
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 := parse(paramValue, paramType)
if err != nil {
return result, err
}
return safeConvert(reflect.ValueOf(value), paramType)
}
}
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
}

View File

@ -0,0 +1,67 @@
package param
import (
"fmt"
"strings"
)
//Keeps param information to be auto passed to controller methods
type MethodParam struct {
name string
location paramLocation
required bool
defValue string
}
type paramLocation byte
const (
param paramLocation = iota
path
body
header
)
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}
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
}

31
context/param/options.go Normal file
View File

@ -0,0 +1,31 @@
package param
import (
"fmt"
)
type MethodParamOption func(*MethodParam)
var IsRequired MethodParamOption = func(p *MethodParam) {
p.required = true
}
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
}
func Default(defValue interface{}) MethodParamOption {
return func(p *MethodParam) {
if defValue != nil {
p.defValue = fmt.Sprint(defValue)
}
}
}

151
context/param/parsers.go Normal file
View File

@ -0,0 +1,151 @@
package param
import (
"encoding/json"
"reflect"
"strconv"
"strings"
"time"
)
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) {
return f(value, toType)
}
type boolParser struct {
}
func (p boolParser) parse(value string, toType reflect.Type) (interface{}, error) {
return strconv.ParseBool(value)
}
type stringParser struct {
}
func (p stringParser) parse(value string, toType reflect.Type) (interface{}, error) {
return value, nil
}
type intParser struct {
}
func (p intParser) parse(value string, toType reflect.Type) (interface{}, error) {
return strconv.Atoi(value)
}
type floatParser struct {
}
func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) {
if toType.Kind() == reflect.Float32 {
res, err := strconv.ParseFloat(value, 32)
if err != nil {
return nil, err
}
return float32(res), nil
}
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
}
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
})
}
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
})
}

View File

@ -0,0 +1,36 @@
package response
import (
"strconv"
beecontext "github.com/astaxie/beego/context"
)
type Renderer interface {
Render(ctx *beecontext.Context)
}
type rendererFunc func(ctx *beecontext.Context)
func (f rendererFunc) Render(ctx *beecontext.Context) {
f(ctx)
}
type StatusCode int
func (s StatusCode) Error() string {
return strconv.Itoa(int(s))
}
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)
}

View File

@ -0,0 +1,50 @@
package response
import (
beecontext "github.com/astaxie/beego/context"
)
func Json(value interface{}, encoding ...bool) Renderer {
return rendererFunc(func(ctx *beecontext.Context) {
var (
hasIndent = true
hasEncoding = false
)
//TODO: need access to BConfig :(
// if BConfig.RunMode == PROD {
// hasIndent = false
// }
if len(encoding) > 0 && encoding[0] {
hasEncoding = true
}
ctx.Output.JSON(value, hasIndent, hasEncoding)
})
}
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 = errorRenderer(err)
} else {
renderer = Json(result)
}
}
renderer.Render(ctx)
}
}