1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-26 22:37:08 +00:00

add the ability to add custom parameter types to the interpreter and mapped macros with any number of macro functions - example added - although it's working it is not ready yet - I have to do some cleanup, doc comments and a TODO

Former-commit-id: 8ac751b649a3b8e59948fd4c89ad53d25f49d0d5
This commit is contained in:
Gerasimos (Makis) Maropoulos
2018-09-26 11:37:11 +03:00
parent 52a07df0f4
commit dc3c38b189
26 changed files with 1070 additions and 1036 deletions

View File

@@ -76,138 +76,6 @@ func (u UnmarshalerFunc) Unmarshal(data []byte, v interface{}) error {
return u(data, v)
}
// RequestParams is a key string - value string storage which
// context's request dynamic path params are being kept.
// Empty if the route is static.
type RequestParams struct {
store memstore.Store
}
// Set adds a key-value pair to the path parameters values
// it's being called internally so it shouldn't be used as a local storage by the user, use `ctx.Values()` instead.
func (r *RequestParams) Set(key, value string) {
r.store.Set(key, value)
}
// Visit accepts a visitor which will be filled
// by the key-value params.
func (r *RequestParams) Visit(visitor func(key string, value string)) {
r.store.Visit(func(k string, v interface{}) {
visitor(k, v.(string)) // always string here.
})
}
var emptyEntry memstore.Entry
// GetEntryAt returns the internal Entry of the memstore based on its index,
// the stored index by the router.
// If not found then it returns a zero Entry and false.
func (r RequestParams) GetEntryAt(index int) (memstore.Entry, bool) {
if len(r.store) > index {
return r.store[index], true
}
return emptyEntry, false
}
// GetEntry returns the internal Entry of the memstore based on its "key".
// If not found then it returns a zero Entry and false.
func (r RequestParams) GetEntry(key string) (memstore.Entry, bool) {
// we don't return the pointer here, we don't want to give the end-developer
// the strength to change the entry that way.
if e := r.store.GetEntry(key); e != nil {
return *e, true
}
return emptyEntry, false
}
// Get returns a path parameter's value based on its route's dynamic path key.
func (r RequestParams) Get(key string) string {
return r.store.GetString(key)
}
// GetTrim returns a path parameter's value without trailing spaces based on its route's dynamic path key.
func (r RequestParams) GetTrim(key string) string {
return strings.TrimSpace(r.Get(key))
}
// GetEscape returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
func (r RequestParams) GetEscape(key string) string {
return DecodeQuery(DecodeQuery(r.Get(key)))
}
// GetDecoded returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
// same as `GetEscape`.
func (r RequestParams) GetDecoded(key string) string {
return r.GetEscape(key)
}
// GetInt returns the path parameter's value as int, based on its key.
// It checks for all available types of int, including int64, strings etc.
// It will return -1 and a non-nil error if parameter wasn't found.
func (r RequestParams) GetInt(key string) (int, error) {
return r.store.GetInt(key)
}
// GetInt64 returns the path paramete's value as int64, based on its key.
// It checks for all available types of int, including int, strings etc.
// It will return -1 and a non-nil error if parameter wasn't found.
func (r RequestParams) GetInt64(key string) (int64, error) {
return r.store.GetInt64(key)
}
// GetFloat64 returns a path parameter's value based as float64 on its route's dynamic path key.
// It checks for all available types of int, including float64, int, strings etc.
// It will return -1 and a non-nil error if parameter wasn't found.
func (r RequestParams) GetFloat64(key string) (float64, error) {
return r.store.GetFloat64(key)
}
// GetUint8 returns the path parameter's value as uint8, based on its key.
// It checks for all available types of int, including int, string.
// It will return 0 and a non-nil error if parameter wasn't found.
func (r RequestParams) GetUint8(key string) (uint8, error) {
return r.store.GetUint8(key)
}
// GetUint64 returns the path parameter's value as uint64, based on its key.
// It checks for all available types of int, including int, uint64, int64, strings etc.
// It will return 0 and a non-nil error if parameter wasn't found.
func (r RequestParams) GetUint64(key string) (uint64, error) {
return r.store.GetUint64(key)
}
// GetBool returns the path parameter's value as bool, based on its key.
// a string which is "1" or "t" or "T" or "TRUE" or "true" or "True"
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
// Any other value returns an error.
func (r RequestParams) GetBool(key string) (bool, error) {
return r.store.GetBool(key)
}
// GetIntUnslashed same as Get but it removes the first slash if found.
// Usage: Get an id from a wildcard path.
//
// Returns -1 with an error if the parameter couldn't be found.
func (r RequestParams) GetIntUnslashed(key string) (int, error) {
v := r.Get(key)
if v != "" {
if len(v) > 1 {
if v[0] == '/' {
v = v[1:]
}
}
return strconv.Atoi(v)
}
return -1, fmt.Errorf("unable to find int for '%s'", key)
}
// Len returns the full length of the parameters.
func (r RequestParams) Len() int {
return r.store.Len()
}
// Context is the midle-man server's "object" for the clients.
//
// A New context is being acquired from a sync.Pool on each connection.
@@ -1123,7 +991,7 @@ func NewContext(app Application) Context {
func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
ctx.handlers = nil // will be filled by router.Serve/HTTP
ctx.values = ctx.values[0:0] // >> >> by context.Values().Set
ctx.params.store = ctx.params.store[0:0]
ctx.params.Store = ctx.params.Store[0:0]
ctx.request = r
ctx.currentHandlerIndex = 0
ctx.writer = AcquireResponseWriter()

159
context/request_params.go Normal file
View File

@@ -0,0 +1,159 @@
package context
import (
"reflect"
"strconv"
"strings"
"github.com/kataras/iris/core/memstore"
)
// RequestParams is a key string - value string storage which
// context's request dynamic path params are being kept.
// Empty if the route is static.
type RequestParams struct {
memstore.Store
}
// GetEntryAt will return the parameter's internal store's `Entry` based on the index.
// If not found it will return an emptry `Entry`.
func (r *RequestParams) GetEntryAt(index int) memstore.Entry {
entry, _ := r.Store.GetEntryAt(index)
return entry
}
// GetEntry will return the parameter's internal store's `Entry` based on its name/key.
// If not found it will return an emptry `Entry`.
func (r *RequestParams) GetEntry(key string) memstore.Entry {
entry, _ := r.Store.GetEntry(key)
return entry
}
// Visit accepts a visitor which will be filled
// by the key-value params.
func (r *RequestParams) Visit(visitor func(key string, value string)) {
r.Store.Visit(func(k string, v interface{}) {
visitor(k, v.(string)) // always string here.
})
}
// Get returns a path parameter's value based on its route's dynamic path key.
func (r RequestParams) Get(key string) string {
return r.GetString(key)
}
// GetTrim returns a path parameter's value without trailing spaces based on its route's dynamic path key.
func (r RequestParams) GetTrim(key string) string {
return strings.TrimSpace(r.Get(key))
}
// GetEscape returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
func (r RequestParams) GetEscape(key string) string {
return DecodeQuery(DecodeQuery(r.Get(key)))
}
// GetDecoded returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
// same as `GetEscape`.
func (r RequestParams) GetDecoded(key string) string {
return r.GetEscape(key)
}
// GetIntUnslashed same as Get but it removes the first slash if found.
// Usage: Get an id from a wildcard path.
//
// Returns -1 and false if not path parameter with that "key" found.
func (r RequestParams) GetIntUnslashed(key string) (int, bool) {
v := r.Get(key)
if v != "" {
if len(v) > 1 {
if v[0] == '/' {
v = v[1:]
}
}
vInt, err := strconv.Atoi(v)
if err != nil {
return -1, false
}
return vInt, true
}
return -1, false
}
var (
ParamResolvers = map[reflect.Kind]func(paramIndex int) interface{}{
reflect.String: func(paramIndex int) interface{} {
return func(ctx Context) string {
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(string)
}
},
reflect.Int: func(paramIndex int) interface{} {
return func(ctx Context) int {
v, _ := ctx.Params().GetEntryAt(paramIndex).IntDefault(0)
return v
}
},
reflect.Int64: func(paramIndex int) interface{} {
return func(ctx Context) int64 {
v, _ := ctx.Params().GetEntryAt(paramIndex).Int64Default(0)
return v
}
},
reflect.Uint8: func(paramIndex int) interface{} {
return func(ctx Context) uint8 {
v, _ := ctx.Params().GetEntryAt(paramIndex).Uint8Default(0)
return v
}
},
reflect.Uint64: func(paramIndex int) interface{} {
return func(ctx Context) uint64 {
v, _ := ctx.Params().GetEntryAt(paramIndex).Uint64Default(0)
return v
}
},
reflect.Bool: func(paramIndex int) interface{} {
return func(ctx Context) bool {
v, _ := ctx.Params().GetEntryAt(paramIndex).BoolDefault(false)
return v
}
},
}
)
// ParamResolverByKindAndIndex 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)
// 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) {
/* NO:
// This could work but its result is not exact type, so direct binding is not possible.
resolver := m.ParamResolver
fn := func(ctx context.Context) interface{} {
entry, _ := ctx.Params().GetEntry(paramName)
return resolver(entry)
}
//
// This works but it is slower on serve-time.
paramNameValue := []reflect.Value{reflect.ValueOf(paramName)}
var fnSignature func(context.Context) string
return reflect.MakeFunc(reflect.ValueOf(&fnSignature).Elem().Type(), func(in []reflect.Value) []reflect.Value {
return in[0].MethodByName("Params").Call(emptyIn)[0].MethodByName("Get").Call(paramNameValue)
// return []reflect.Value{reflect.ValueOf(in[0].Interface().(context.Context).Params().Get(paramName))}
})
//
*/
r, ok := ParamResolvers[k]
if !ok || r == nil {
return reflect.Value{}, false
}
return reflect.ValueOf(r(paramIndex)), true
}