mirror of
https://github.com/kataras/iris.git
synced 2026-01-09 13:05:56 +00:00
Version 11 released. Read https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v1100
Former-commit-id: fe6305deed00e170bf4d39a12c0644fe686e0a24
This commit is contained in:
@@ -76,131 +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)
|
||||
}
|
||||
|
||||
// GetUint64 returns the path paramete'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.
|
||||
@@ -685,7 +560,8 @@ type Context interface {
|
||||
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go
|
||||
ReadXML(xmlObjectPtr interface{}) error
|
||||
// ReadForm binds the formObject with the form data
|
||||
// it supports any kind of struct.
|
||||
// it supports any kind of type, including custom structs.
|
||||
// It will return nothing if request data are empty.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go
|
||||
ReadForm(formObjectPtr interface{}) error
|
||||
@@ -1116,7 +992,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()
|
||||
@@ -2414,17 +2290,15 @@ var (
|
||||
errReadBody = errors.New("while trying to read %s from the request body. Trace %s")
|
||||
)
|
||||
|
||||
// ErrEmptyForm will be thrown from `context#ReadForm` when empty request data.
|
||||
var ErrEmptyForm = errors.New("form data: empty")
|
||||
|
||||
// ReadForm binds the formObject with the form data
|
||||
// it supports any kind of struct.
|
||||
// it supports any kind of type, including custom structs.
|
||||
// It will return nothing if request data are empty.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go
|
||||
func (ctx *context) ReadForm(formObject interface{}) error {
|
||||
values := ctx.FormValues()
|
||||
if values == nil {
|
||||
return ErrEmptyForm
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// or dec := formbinder.NewDecoder(&formbinder.DecoderOptions{TagName: "form"})
|
||||
@@ -2990,12 +2864,10 @@ func (ctx *context) JSON(v interface{}, opts ...JSON) (n int, err error) {
|
||||
options = opts[0]
|
||||
}
|
||||
|
||||
optimize := ctx.shouldOptimize()
|
||||
|
||||
ctx.ContentType(ContentJSONHeaderValue)
|
||||
|
||||
if options.StreamingJSON {
|
||||
if optimize {
|
||||
if ctx.shouldOptimize() {
|
||||
var jsoniterConfig = jsoniter.Config{
|
||||
EscapeHTML: !options.UnescapeHTML,
|
||||
IndentionStep: 4,
|
||||
@@ -3016,7 +2888,7 @@ func (ctx *context) JSON(v interface{}, opts ...JSON) (n int, err error) {
|
||||
return ctx.writer.Written(), err
|
||||
}
|
||||
|
||||
n, err = WriteJSON(ctx.writer, v, options, optimize)
|
||||
n, err = WriteJSON(ctx.writer, v, options, ctx.shouldOptimize())
|
||||
if err != nil {
|
||||
ctx.StatusCode(http.StatusInternalServerError)
|
||||
return 0, err
|
||||
|
||||
206
context/request_params.go
Normal file
206
context/request_params.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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
|
||||
}
|
||||
|
||||
// Set inserts a value to the key-value storage.
|
||||
//
|
||||
// See `SetImmutable` and `Get` too.
|
||||
func (r *RequestParams) Set(key, value string) {
|
||||
r.Store.Set(key, value)
|
||||
}
|
||||
|
||||
// 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, fmt.Sprintf("%v", v)) // 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 is the global param resolution for a parameter type for a specific go std or custom type.
|
||||
//
|
||||
// Key is the specific type, which should be unique.
|
||||
// The value is a function which accepts the parameter index
|
||||
// and it should return the value as the parameter type evaluator expects it.
|
||||
// i.e [reflect.TypeOf("string")] = func(paramIndex int) interface{} {
|
||||
// return func(ctx Context) <T> {
|
||||
// return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(<T>)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Read https://github.com/kataras/iris/tree/master/_examples/routing/macros for more details.
|
||||
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.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.TypeOf(int8(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int8 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int8)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(int16(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int16 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int16)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(int32(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int32 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int32)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(int64(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) int64 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int64)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(uint(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(uint8(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint8 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint8)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(uint16(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint16 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint16)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(uint32(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint32 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint32)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(uint64(1)): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) uint64 {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint64)
|
||||
}
|
||||
},
|
||||
reflect.TypeOf(true): func(paramIndex int) interface{} {
|
||||
return func(ctx Context) bool {
|
||||
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(bool)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// 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.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 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
|
||||
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[typ]
|
||||
if !ok || r == nil {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
return reflect.ValueOf(r(paramIndex)), true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package context
|
||||
|
||||
import "github.com/kataras/iris/core/router/macro"
|
||||
import "github.com/kataras/iris/macro"
|
||||
|
||||
// RouteReadOnly allows decoupled access to the current route
|
||||
// inside the context.
|
||||
@@ -25,7 +25,7 @@ type RouteReadOnly interface {
|
||||
|
||||
// StaticPath returns the static part of the original, registered route path.
|
||||
// if /user/{id} it will return /user
|
||||
// if /user/{id}/friend/{friendid:int} it will return /user too
|
||||
// if /user/{id}/friend/{friendid:uint64} it will return /user too
|
||||
// if /assets/{filepath:path} it will return /assets.
|
||||
StaticPath() string
|
||||
|
||||
|
||||
Reference in New Issue
Block a user