mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 23:40:35 +01:00
support more than string and int at macro functions route path input arguments: int,uint8,uint16,uint32,int8,int32,int64,slice of strings and string
Former-commit-id: d29c4fbe5926bac590151322a585f68b394ff72d
This commit is contained in:
parent
18c23e7d1e
commit
ef5f383227
|
@ -112,16 +112,16 @@ func main() {
|
||||||
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
||||||
}) // type is missing = {name:string}
|
}) // type is missing = {name:string}
|
||||||
|
|
||||||
// Let's register our first macro attached to number macro type.
|
// Let's register our first macro attached to uint64 macro type.
|
||||||
// "min" = the function
|
// "min" = the function
|
||||||
// "minValue" = the argument of the function
|
// "minValue" = the argument of the function
|
||||||
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
||||||
// a user requests a path which contains the :number macro type with the min(...) macro parameter function.
|
// a user requests a path which contains the :uint64 macro parameter type with the min(...) macro parameter function.
|
||||||
app.Macros().Number.RegisterFunc("min", func(minValue int) func(string) bool {
|
app.Macros().Uint64.RegisterFunc("min", func(minValue uint64) func(string) bool {
|
||||||
// do anything before serve here [...]
|
// do anything before serve here [...]
|
||||||
// at this case we don't need to do anything
|
// at this case we don't need to do anything
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
n, err := strconv.Atoi(paramValue)
|
n, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -129,10 +129,10 @@ func main() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// http://localhost:8080/profile/id>=1
|
// http://localhost:8080/profile/id>=20
|
||||||
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
||||||
// macro parameter functions are optional of course.
|
// macro parameter functions are optional of course.
|
||||||
app.Get("/profile/{id:number min(1)}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(20)}", func(ctx iris.Context) {
|
||||||
// second parameter is the error but it will always nil because we use macros,
|
// second parameter is the error but it will always nil because we use macros,
|
||||||
// the validaton already happened.
|
// the validaton already happened.
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetInt("id")
|
||||||
|
@ -140,7 +140,7 @@ func main() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// to change the error code per route's macro evaluator:
|
// to change the error code per route's macro evaluator:
|
||||||
app.Get("/profile/{id:number min(1)}/friends/{friendid:number min(1) else 504}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id") // or GetUint64.
|
id, _ := ctx.Params().GetInt("id") // or GetUint64.
|
||||||
friendid, _ := ctx.Params().GetInt("friendid")
|
friendid, _ := ctx.Params().GetInt("friendid")
|
||||||
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParamType is a specific uint8 type
|
// ParamType is a specific uint8 type
|
||||||
|
@ -206,24 +204,6 @@ type ParamStatement struct {
|
||||||
ErrorCode int // 404
|
ErrorCode int // 404
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParamFuncArg represents a single parameter function's argument
|
|
||||||
type ParamFuncArg interface{}
|
|
||||||
|
|
||||||
// ParamFuncArgToInt converts and returns
|
|
||||||
// any type of "a", to an integer.
|
|
||||||
func ParamFuncArgToInt(a ParamFuncArg) (int, error) {
|
|
||||||
switch a.(type) {
|
|
||||||
case int:
|
|
||||||
return a.(int), nil
|
|
||||||
case string:
|
|
||||||
return strconv.Atoi(a.(string))
|
|
||||||
case int64:
|
|
||||||
return int(a.(int64)), nil
|
|
||||||
default:
|
|
||||||
return -1, fmt.Errorf("unexpected function argument type: %q", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParamFunc holds the name of a parameter's function
|
// ParamFunc holds the name of a parameter's function
|
||||||
// and its arguments (values)
|
// and its arguments (values)
|
||||||
// A param func is declared with:
|
// A param func is declared with:
|
||||||
|
@ -233,6 +213,6 @@ func ParamFuncArgToInt(a ParamFuncArg) (int, error) {
|
||||||
// the 1 and 5 are the two param function arguments
|
// the 1 and 5 are the two param function arguments
|
||||||
// range(1,5)
|
// range(1,5)
|
||||||
type ParamFunc struct {
|
type ParamFunc struct {
|
||||||
Name string // range
|
Name string // range
|
||||||
Args []ParamFuncArg // [1,5]
|
Args []string // ["1","5"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,10 +82,16 @@ const (
|
||||||
DefaultParamType = ast.ParamTypeString
|
DefaultParamType = ast.ParamTypeString
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseParamFuncArg(t token.Token) (a ast.ParamFuncArg, err error) {
|
// func parseParamFuncArg(t token.Token) (a ast.ParamFuncArg, err error) {
|
||||||
if t.Type == token.INT {
|
// if t.Type == token.INT {
|
||||||
return ast.ParamFuncArgToInt(t.Literal)
|
// return ast.ParamFuncArgToInt(t.Literal)
|
||||||
}
|
// }
|
||||||
|
// // act all as strings here, because of int vs int64 vs uint64 and etc.
|
||||||
|
// return t.Literal, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func parseParamFuncArg(t token.Token) (a string, err error) {
|
||||||
|
// act all as strings here, because of int vs int64 vs uint64 and etc.
|
||||||
return t.Literal, nil
|
return t.Literal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,25 +149,14 @@ func (p *ParamParser) Parse() (*ast.ParamStatement, error) {
|
||||||
|
|
||||||
argValTok := l.NextDynamicToken() // catch anything from "(" and forward, until ")", because we need to
|
argValTok := l.NextDynamicToken() // catch anything from "(" and forward, until ")", because we need to
|
||||||
// be able to use regex expression as a macro type's func argument too.
|
// be able to use regex expression as a macro type's func argument too.
|
||||||
argVal, err := parseParamFuncArg(argValTok)
|
|
||||||
if err != nil {
|
|
||||||
p.appendErr("[%d:%d] expected param func argument to be a string or number but got %s", t.Start, t.End, argValTok.Literal)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmt.Printf("argValTok: %#v\n", argValTok)
|
// fmt.Printf("argValTok: %#v\n", argValTok)
|
||||||
// fmt.Printf("argVal: %#v\n", argVal)
|
// fmt.Printf("argVal: %#v\n", argVal)
|
||||||
lastParamFunc.Args = append(lastParamFunc.Args, argVal)
|
lastParamFunc.Args = append(lastParamFunc.Args, argValTok.Literal)
|
||||||
|
|
||||||
case token.COMMA:
|
case token.COMMA:
|
||||||
argValTok := l.NextToken()
|
argValTok := l.NextToken()
|
||||||
argVal, err := parseParamFuncArg(argValTok)
|
lastParamFunc.Args = append(lastParamFunc.Args, argValTok.Literal)
|
||||||
if err != nil {
|
|
||||||
p.appendErr("[%d:%d] expected param func argument to be a string or number type but got %s", t.Start, t.End, argValTok.Literal)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lastParamFunc.Args = append(lastParamFunc.Args, argVal)
|
|
||||||
case token.RPAREN:
|
case token.RPAREN:
|
||||||
stmt.Funcs = append(stmt.Funcs, lastParamFunc)
|
stmt.Funcs = append(stmt.Funcs, lastParamFunc)
|
||||||
lastParamFunc = ast.ParamFunc{} // reset
|
lastParamFunc = ast.ParamFunc{} // reset
|
||||||
|
|
|
@ -53,10 +53,10 @@ func TestParseParam(t *testing.T) {
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "min",
|
Name: "min",
|
||||||
Args: []ast.ParamFuncArg{1}},
|
Args: []string{"1"}},
|
||||||
{
|
{
|
||||||
Name: "max",
|
Name: "max",
|
||||||
Args: []ast.ParamFuncArg{5}},
|
Args: []string{"5"}},
|
||||||
},
|
},
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 0
|
}}, // 0
|
||||||
|
@ -69,7 +69,7 @@ func TestParseParam(t *testing.T) {
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "range",
|
Name: "range",
|
||||||
Args: []ast.ParamFuncArg{1, 5}},
|
Args: []string{"1", "5"}},
|
||||||
},
|
},
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 1
|
}}, // 1
|
||||||
|
@ -81,7 +81,7 @@ func TestParseParam(t *testing.T) {
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "contains",
|
Name: "contains",
|
||||||
Args: []ast.ParamFuncArg{"."}},
|
Args: []string{"."}},
|
||||||
},
|
},
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 2
|
}}, // 2
|
||||||
|
@ -189,10 +189,10 @@ func TestParse(t *testing.T) {
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "min",
|
Name: "min",
|
||||||
Args: []ast.ParamFuncArg{1}},
|
Args: []string{"1"}},
|
||||||
{
|
{
|
||||||
Name: "max",
|
Name: "max",
|
||||||
Args: []ast.ParamFuncArg{5}},
|
Args: []string{"5"}},
|
||||||
},
|
},
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
},
|
},
|
||||||
|
@ -205,7 +205,7 @@ func TestParse(t *testing.T) {
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "range",
|
Name: "range",
|
||||||
Args: []ast.ParamFuncArg{1, 5}},
|
Args: []string{"1", "5"}},
|
||||||
},
|
},
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
},
|
},
|
||||||
|
@ -218,7 +218,7 @@ func TestParse(t *testing.T) {
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "contains",
|
Name: "contains",
|
||||||
Args: []ast.ParamFuncArg{"."}},
|
Args: []string{"."}},
|
||||||
},
|
},
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||||
|
@ -95,7 +96,7 @@ func convertBuilderFunc(fn interface{}) ParamEvaluatorBuilder {
|
||||||
|
|
||||||
numFields := typFn.NumIn()
|
numFields := typFn.NumIn()
|
||||||
|
|
||||||
return func(args []ast.ParamFuncArg) EvaluatorFunc {
|
return func(args []string) EvaluatorFunc {
|
||||||
if len(args) != numFields {
|
if len(args) != numFields {
|
||||||
// no variadics support, for now.
|
// no variadics support, for now.
|
||||||
panic("args should be the same len as numFields")
|
panic("args should be the same len as numFields")
|
||||||
|
@ -105,11 +106,60 @@ func convertBuilderFunc(fn interface{}) ParamEvaluatorBuilder {
|
||||||
field := typFn.In(i)
|
field := typFn.In(i)
|
||||||
arg := args[i]
|
arg := args[i]
|
||||||
|
|
||||||
if field.Kind() != reflect.TypeOf(arg).Kind() {
|
// try to convert the string literal as we get it from the parser.
|
||||||
panic("fields should have the same type")
|
var (
|
||||||
|
v interface{}
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// try to get the value based on the expected type.
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Int:
|
||||||
|
v, err = strconv.Atoi(arg)
|
||||||
|
case reflect.Int8:
|
||||||
|
v, err = strconv.ParseInt(arg, 10, 8)
|
||||||
|
case reflect.Int16:
|
||||||
|
v, err = strconv.ParseInt(arg, 10, 16)
|
||||||
|
case reflect.Int32:
|
||||||
|
v, err = strconv.ParseInt(arg, 10, 32)
|
||||||
|
case reflect.Int64:
|
||||||
|
v, err = strconv.ParseInt(arg, 10, 64)
|
||||||
|
case reflect.Uint8:
|
||||||
|
v, err = strconv.ParseUint(arg, 10, 8)
|
||||||
|
case reflect.Uint16:
|
||||||
|
v, err = strconv.ParseUint(arg, 10, 16)
|
||||||
|
case reflect.Uint32:
|
||||||
|
v, err = strconv.ParseUint(arg, 10, 32)
|
||||||
|
case reflect.Uint64:
|
||||||
|
v, err = strconv.ParseUint(arg, 10, 64)
|
||||||
|
case reflect.Float32:
|
||||||
|
v, err = strconv.ParseFloat(arg, 32)
|
||||||
|
case reflect.Float64:
|
||||||
|
v, err = strconv.ParseFloat(arg, 64)
|
||||||
|
case reflect.Bool:
|
||||||
|
v, err = strconv.ParseBool(arg)
|
||||||
|
case reflect.Slice:
|
||||||
|
if len(arg) > 1 {
|
||||||
|
if arg[0] == '[' && arg[len(arg)-1] == ']' {
|
||||||
|
// it is a single argument but as slice.
|
||||||
|
v = strings.Split(arg[1:len(arg)-1], ",") // only string slices.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
v = arg
|
||||||
}
|
}
|
||||||
|
|
||||||
argValues = append(argValues, reflect.ValueOf(arg))
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("on field index: %d: %v", i, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
argValue := reflect.ValueOf(v)
|
||||||
|
if expected, got := field.Kind(), argValue.Kind(); expected != got {
|
||||||
|
panic(fmt.Sprintf("fields should have the same type: [%d] expected %s but got %s", i, expected, got))
|
||||||
|
}
|
||||||
|
|
||||||
|
argValues = append(argValues, argValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
evalFn := reflect.ValueOf(fn).Call(argValues)[0].Interface()
|
evalFn := reflect.ValueOf(fn).Call(argValues)[0].Interface()
|
||||||
|
@ -149,7 +199,7 @@ type (
|
||||||
// and returns an EvaluatorFunc, its job
|
// and returns an EvaluatorFunc, its job
|
||||||
// is to make the macros to be registered
|
// is to make the macros to be registered
|
||||||
// by user at the most generic possible way.
|
// by user at the most generic possible way.
|
||||||
ParamEvaluatorBuilder func([]ast.ParamFuncArg) EvaluatorFunc
|
ParamEvaluatorBuilder func([]string) EvaluatorFunc
|
||||||
|
|
||||||
// ParamFunc represents the parsed
|
// ParamFunc represents the parsed
|
||||||
// parameter function, it holds
|
// parameter function, it holds
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestGoodParamFunc(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
good2 := func(min int, max int) func(string) bool {
|
good2 := func(min uint64, max uint64) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -244,3 +244,37 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||||
|
|
||||||
// testEvaluatorRaw(t, m.String, p.Src, false, 0)
|
// testEvaluatorRaw(t, m.String, p.Src, false, 0)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
func TestConvertBuilderFunc(t *testing.T) {
|
||||||
|
fn := func(min uint64, slice []string) func(string) bool {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
if expected, got := "ok", paramValue; expected != got {
|
||||||
|
t.Fatalf("paramValue is not the expected one: %s vs %s", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected, got := uint64(1), min; expected != got {
|
||||||
|
t.Fatalf("min argument is not the expected one: %d vs %d", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected, got := []string{"name1", "name2"}, slice; len(expected) == len(got) {
|
||||||
|
if expected, got := "name1", slice[0]; expected != got {
|
||||||
|
t.Fatalf("slice argument[%d] does not contain the expected value: %s vs %s", 0, expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected, got := "name2", slice[1]; expected != got {
|
||||||
|
t.Fatalf("slice argument[%d] does not contain the expected value: %s vs %s", 1, expected, got)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("slice argument is not the expected one, the length is difference: %d vs %d", len(expected), len(got))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evalFunc := convertBuilderFunc(fn)
|
||||||
|
|
||||||
|
if !evalFunc([]string{"1", "[name1,name2]"})("ok") {
|
||||||
|
t.Fatalf("failed, it should fail already")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user