mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
fix macro registration issue and match by kind for MVC and hero instead of its kind, so custom types like structs can be used without any issues. Add an example on how to register a custom macro it is just few lines and all in one place in this version.
Former-commit-id: 93c439560fcfad820f9f3e39c1d9557c83cef0ee
This commit is contained in:
parent
21ab51bde7
commit
a675e8191a
|
@ -1,12 +1,14 @@
|
|||
// Package main shows how you can register a custom parameter type and macro functions that belongs to it.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
// "github.com/kataras/iris/core/memstore"
|
||||
"github.com/kataras/iris/hero"
|
||||
)
|
||||
|
||||
|
@ -14,80 +16,60 @@ func main() {
|
|||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// Let's see how we can register a custom macro such as ":uint32" or ":small" for its alias (optionally) for Uint32 types.
|
||||
// app.Macros().Register("uint32", "small", false, false, func(paramValue string) bool {
|
||||
// _, err := strconv.ParseUint(paramValue, 10, 32)
|
||||
// return err == nil
|
||||
// }).
|
||||
// RegisterFunc("min", func(min uint32) func(string) bool {
|
||||
// return func(paramValue string) bool {
|
||||
// n, err := strconv.ParseUint(paramValue, 10, 32)
|
||||
// if err != nil {
|
||||
// return false
|
||||
// }
|
||||
app.Macros().Register("slice", "", false, true, func(paramValue string) (interface{}, bool) {
|
||||
return strings.Split(paramValue, "/"), true
|
||||
}).RegisterFunc("contains", func(expectedItems []string) func(paramValue []string) bool {
|
||||
sort.Strings(expectedItems)
|
||||
return func(paramValue []string) bool {
|
||||
if len(paramValue) != len(expectedItems) {
|
||||
return false
|
||||
}
|
||||
|
||||
// return uint32(n) >= min
|
||||
// }
|
||||
// })
|
||||
sort.Strings(paramValue)
|
||||
for i := 0; i < len(paramValue); i++ {
|
||||
if paramValue[i] != expectedItems[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
somehow define one-time how the parameter should be parsed to a particular type (go std or custom)
|
||||
tip: we can change the original value from string to X using the entry's.ValueRaw
|
||||
^ Done 27 sep 2018.
|
||||
*/
|
||||
|
||||
// app.Macros().Register("uint32", "small", false, false, func(paramValue string) (interface{}, bool) {
|
||||
// v, err := strconv.ParseUint(paramValue, 10, 32)
|
||||
// return uint32(v), err == nil
|
||||
// }).
|
||||
// RegisterFunc("min", func(min uint32) func(uint32) bool {
|
||||
// return func(paramValue uint32) bool {
|
||||
// return paramValue >= min
|
||||
// }
|
||||
// })
|
||||
|
||||
// // optionally, only when mvc or hero features are used for this custom macro/parameter type.
|
||||
// context.ParamResolvers[reflect.Uint32] = func(paramIndex int) interface{} {
|
||||
// /* both works but second is faster, we omit the duplication of the type conversion over and over as of 27 Sep of 2018 (this patch)*/
|
||||
// // return func(ctx context.Context) uint32 {
|
||||
// // param := ctx.Params().GetEntryAt(paramIndex)
|
||||
// // paramValueAsUint32, _ := strconv.ParseUint(param.String(), 10, 32)
|
||||
// // return uint32(paramValueAsUint32)
|
||||
// // }
|
||||
// return func(ctx context.Context) uint32 {
|
||||
// return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint32)
|
||||
// } /* TODO: find a way to automative it based on the macro's first return value type, if thats the case then we must not return nil even if not found,
|
||||
// we must return a value i.e 0 for int for its interface{} */
|
||||
// }
|
||||
// //
|
||||
|
||||
app.Get("/test_uint32/{myparam1:string}/{myparam2:uint32 min(10)}", hero.Handler(func(myparam1 string, myparam2 uint32) string {
|
||||
return fmt.Sprintf("Value of the parameters are: %s:%d\n", myparam1, myparam2)
|
||||
}))
|
||||
|
||||
app.Get("/test_string/{myparam1}/{myparam2 prefix(a)}", func(ctx context.Context) {
|
||||
var (
|
||||
myparam1 = ctx.Params().Get("myparam1")
|
||||
myparam2 = ctx.Params().Get("myparam2")
|
||||
)
|
||||
|
||||
ctx.Writef("myparam1: %s | myparam2: %s", myparam1, myparam2)
|
||||
}, func(ctx context.Context) {})
|
||||
|
||||
app.Get("/test_string2/{myparam1}/{myparam2}", func(ctx context.Context) {
|
||||
var (
|
||||
myparam1 = ctx.Params().Get("myparam1")
|
||||
myparam2 = ctx.Params().Get("myparam2")
|
||||
)
|
||||
|
||||
ctx.Writef("myparam1: %s | myparam2: %s", myparam1, myparam2)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
app.Get("/test_uint64/{myparam1:string}/{myparam2:uint64}", func(ctx context.Context) {
|
||||
// works: ctx.Writef("Value of the parameter is: %s\n", ctx.Params().Get("myparam"))
|
||||
// but better and faster because the macro converts the string to uint64 automatically:
|
||||
println("type of myparam2 (should be uint64) is: " + reflect.ValueOf(ctx.Params().GetEntry("myparam2").ValueRaw).Kind().String())
|
||||
ctx.Writef("Value of the parameters are: %s:%d\n", ctx.Params().Get("myparam1"), ctx.Params().GetUint64Default("myparam2", 0))
|
||||
// In order to use your new param type inside MVC controller's function input argument or a hero function input argument
|
||||
// you have to tell the Iris what type it is, the `ValueRaw` of the parameter is the same type
|
||||
// as you defined it above with the func(paramValue string) (interface{}, bool).
|
||||
// The new value and its type(from string to your new custom type) it is stored only once now,
|
||||
// you don't have to do any conversions for simple cases like this.
|
||||
context.ParamResolvers[reflect.TypeOf([]string{})] = func(paramIndex int) interface{} {
|
||||
return func(ctx context.Context) []string {
|
||||
// When you want to retrieve a parameter with a value type that it is not supported by-default, such as ctx.Params().GetInt
|
||||
// then you can use the `GetEntry` or `GetEntryAt` and cast its underline `ValueRaw` to the desired type.
|
||||
// The type should be the same as the macro's evaluator function (last argument on the Macros#Register) return value.
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.([]string)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
http://localhost:8080/test_slice_hero/myvaluei1/myavlue2 ->
|
||||
myparam's value (a trailing path parameter type) is: []string{"myvaluei1", "myavlue2"}
|
||||
*/
|
||||
app.Get("/test_slice_hero/{myparam:slice}", hero.Handler(func(myparam []string) string {
|
||||
return fmt.Sprintf("myparam's value (a trailing path parameter type) is: %#v\n", myparam)
|
||||
}))
|
||||
|
||||
/*
|
||||
http://localhost:8080/test_slice_contains/notcontains1/value2 ->
|
||||
(404) Not Found
|
||||
|
||||
http://localhost:8080/test_slice_contains/value1/value2 ->
|
||||
myparam's value (a trailing path parameter type) is: []string{"value1", "value2"}
|
||||
*/
|
||||
app.Get("/test_slice_contains/{myparam:slice contains([value1,value2])}", func(ctx context.Context) {
|
||||
// When it is not a built'n function available to retrieve your value with the type you want, such as ctx.Params().GetInt
|
||||
// then you can use the `GetEntry.ValueRaw` to get the real value, which is set-ed by your macro above.
|
||||
myparam := ctx.Params().GetEntry("myparam").ValueRaw.([]string)
|
||||
ctx.Writef("myparam's value (a trailing path parameter type) is: %#v\n", myparam)
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
|
|
@ -83,65 +83,65 @@ func (r RequestParams) GetIntUnslashed(key string) (int, bool) {
|
|||
}
|
||||
|
||||
var (
|
||||
ParamResolvers = map[reflect.Kind]func(paramIndex int) interface{}{
|
||||
reflect.String: func(paramIndex int) interface{} {
|
||||
ParamResolvers = map[reflect.Type]func(paramIndex int) interface{}{
|
||||
reflect.TypeOf(""): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) string {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(string)
|
||||
}
|
||||
},
|
||||
reflect.Int: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(int(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int {
|
||||
// v, _ := ctx.Params().GetEntryAt(paramIndex).IntDefault(0)
|
||||
// return v
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int)
|
||||
}
|
||||
},
|
||||
reflect.Int8: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(int8(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int8 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int8)
|
||||
}
|
||||
},
|
||||
reflect.Int16: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(int16(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int16 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int16)
|
||||
}
|
||||
},
|
||||
reflect.Int32: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(int32(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int32 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int32)
|
||||
}
|
||||
},
|
||||
reflect.Int64: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(int64(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int64 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int64)
|
||||
}
|
||||
},
|
||||
reflect.Uint: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(uint(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint)
|
||||
}
|
||||
},
|
||||
reflect.Uint8: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(uint8(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint8 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint8)
|
||||
}
|
||||
},
|
||||
reflect.Uint16: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(uint16(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint16 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint16)
|
||||
}
|
||||
},
|
||||
reflect.Uint32: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(uint32(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint32 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint32)
|
||||
}
|
||||
},
|
||||
reflect.Uint64: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(uint64(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint64 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint64)
|
||||
}
|
||||
},
|
||||
reflect.Bool: func(paramIndex int) interface{} {
|
||||
reflect.TypeOf(true): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) bool {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(bool)
|
||||
}
|
||||
|
@ -149,16 +149,16 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// ParamResolverByKindAndIndex will return a function that can be used to bind path parameter's exact value by its Go std type
|
||||
// ParamResolverByTypeAndIndex will return a function that can be used to bind path parameter's exact value by its Go std type
|
||||
// and the parameter's index based on the registered path.
|
||||
// Usage: nameResolver := ParamResolverByKindAndKey(reflect.String, 0)
|
||||
// Usage: nameResolver := ParamResolverByKindAndKey(reflect.TypeOf(""), 0)
|
||||
// Inside a Handler: nameResolver.Call(ctx)[0]
|
||||
// it will return the reflect.Value Of the exact type of the parameter(based on the path parameters and macros).
|
||||
// It is only useful for dynamic binding of the parameter, it is used on "hero" package and it should be modified
|
||||
// only when Macros are modified in such way that the default selections for the available go std types are not enough.
|
||||
//
|
||||
// Returns empty value and false if "k" does not match any valid parameter resolver.
|
||||
func ParamResolverByKindAndIndex(k reflect.Kind, paramIndex int) (reflect.Value, bool) {
|
||||
func ParamResolverByTypeAndIndex(typ reflect.Type, paramIndex int) (reflect.Value, bool) {
|
||||
/* NO:
|
||||
// This could work but its result is not exact type, so direct binding is not possible.
|
||||
resolver := m.ParamResolver
|
||||
|
@ -178,7 +178,7 @@ func ParamResolverByKindAndIndex(k reflect.Kind, paramIndex int) (reflect.Value,
|
|||
//
|
||||
*/
|
||||
|
||||
r, ok := ParamResolvers[k]
|
||||
r, ok := ParamResolvers[typ]
|
||||
if !ok || r == nil {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ type params struct {
|
|||
|
||||
func (p *params) resolve(index int, typ reflect.Type) (reflect.Value, bool) {
|
||||
currentParamIndex := p.next
|
||||
v, ok := context.ParamResolverByKindAndIndex(typ.Kind(), currentParamIndex)
|
||||
v, ok := context.ParamResolverByTypeAndIndex(typ, currentParamIndex)
|
||||
|
||||
p.next = p.next + 1
|
||||
return v, ok
|
||||
|
|
4
macro/AUTHORS
Normal file
4
macro/AUTHORS
Normal file
|
@ -0,0 +1,4 @@
|
|||
# This is the official list of Iris Macro and Route path interpreter authors for copyright
|
||||
# purposes.
|
||||
|
||||
Gerasimos Maropoulos <kataras2006@hotmail.com>
|
27
macro/LICENSE
Normal file
27
macro/LICENSE
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2017-2018 The Iris Macro and Route path interpreter. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Iris nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -10,10 +10,10 @@ import (
|
|||
var (
|
||||
// String type
|
||||
// Allows anything (single path segment, as everything except the `Path`).
|
||||
// Its functions can be used by the rest of the macros and param types whenever not available function by name is used.
|
||||
// Because of its "master" boolean value to true (third parameter).
|
||||
String = NewMacro("string", "", true, false, nil).
|
||||
RegisterFunc("regexp", func(expr string) func(string) bool {
|
||||
return MustRegexp(expr)
|
||||
}).
|
||||
RegisterFunc("regexp", MustRegexp).
|
||||
// checks if param value starts with the 'prefix' arg
|
||||
RegisterFunc("prefix", func(prefix string) func(string) bool {
|
||||
return func(paramValue string) bool {
|
||||
|
@ -431,21 +431,22 @@ func (ms *Macros) Register(indent, alias string, isMaster, isTrailing bool, eval
|
|||
}
|
||||
|
||||
func (ms *Macros) register(macro *Macro) bool {
|
||||
if macro.Indent() == "" || macro.Evaluator == nil {
|
||||
if macro.Indent() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
cp := *ms
|
||||
|
||||
for _, m := range cp {
|
||||
|
||||
// can't add more than one with the same ast characteristics.
|
||||
if macro.Indent() == m.Indent() {
|
||||
return false
|
||||
}
|
||||
|
||||
if macro.Alias() == m.Alias() || macro.Alias() == m.Indent() {
|
||||
return false
|
||||
if alias := macro.Alias(); alias != "" {
|
||||
if alias == m.Alias() || alias == m.Indent() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if macro.Master() && m.Master() {
|
||||
|
|
|
@ -39,7 +39,7 @@ func getPathParamsForInput(params []macro.TemplateParam, funcIn ...reflect.Type)
|
|||
if len(funcIn) <= i {
|
||||
return
|
||||
}
|
||||
funcDep, ok := context.ParamResolverByKindAndIndex(funcIn[i].Kind(), param.Index)
|
||||
funcDep, ok := context.ParamResolverByTypeAndIndex(funcIn[i], param.Index)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user