Newer
Older
if rctx := RouteContext(r.Context()); rctx != nil {
return rctx.MethodParam(key)
}
return ""
}
func MethodParamFromCtx(ctx context.Context, key string) string {
if rctx := RouteContext(ctx); rctx != nil {
return rctx.MethodParam(key)
}
return ""
}
// RouteContext returns chi's routing Context object from a
func RouteContext(ctx context.Context) *Context {
val, _ := ctx.Value(RouteCtxKey).(*Context)
return val
}
// NewRouteContext returns a new routing Context object.
func NewRouteContext() *Context {
return &Context{}
}
var (
// RouteCtxKey is the context.Context key to store the request context.
RouteCtxKey = &contextKey{"RouteContext"}
)
// Context is the default routing context set on the root node of a
// request context to track route patterns, Method parameters and
// an optional routing path.
type Context struct {
Routes Routes
// parentCtx is the parent of this one, for using Context as a
// context.Context directly. This is an optimization that saves
// 1 allocation.
parentCtx context.Context
// Routing path/method override used during the route search.
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
RoutePath string
RouteMethod string
// MethodParams are the stack of routeParams captured during the
// routing lifecycle across a stack of sub-routers.
MethodParams RouteParams
// Route parameters matched for the current sub-router. It is
// intentionally unexported so it cant be tampered.
routeParams RouteParams
// The endpoint routing pattern that matched the request URI path
// or `RoutePath` of the current sub-router. This value will update
// during the lifecycle of a request passing through a stack of
// sub-routers.
routePattern string
// Routing pattern stack throughout the lifecycle of the request,
// across all connected routers. It is a record of all matching
// patterns across a stack of sub-routers.
RoutePatterns []string
// methodNotAllowed hint
methodNotAllowed bool
}
// Reset a routing context to its initial state.
func (x *Context) Reset() {
x.Routes = nil
x.RoutePath = ""
x.RouteMethod = ""
x.RoutePatterns = x.RoutePatterns[:0]
x.MethodParams.Keys = x.MethodParams.Keys[:0]
x.MethodParams.Values = x.MethodParams.Values[:0]
x.routePattern = ""
x.routeParams.Keys = x.routeParams.Keys[:0]
x.routeParams.Values = x.routeParams.Values[:0]
x.methodNotAllowed = false
x.parentCtx = nil
}
// MethodParam returns the corresponding Method parameter value from the request
// routing context.
func (x *Context) MethodParam(key string) string {
for k := len(x.MethodParams.Keys) - 1; k >= 0; k-- {
if x.MethodParams.Keys[k] == key {
return x.MethodParams.Values[k]
}
}
return ""
}
// RoutePattern builds the routing pattern string for the particular
// request, at the particular point during routing. This means, the value
// will change throughout the execution of a request in a router. That is
// why its advised to only use this value after calling the next handler.
//
// For example,
//
// func Instrument(next Handler) Handler {
// return HandlerFunc(func(w ResponseWriter, r *Request) {
// next.Servew, r)
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
// measure(w, r, routePattern)
// })
// }
func (x *Context) RoutePattern() string {
routePattern := strings.Join(x.RoutePatterns, "")
routePattern = replaceWildcards(routePattern)
return routePattern
}
// replaceWildcards takes a route pattern and recursively replaces all
if strings.Contains(p, sepString+"*"+sepString) {
return replaceWildcards(strings.Replace(p, sepString+"*"+sepString, sepString, -1))
}
return p
}
// RouteParams is a structure to track Method routing parameters efficiently.
type RouteParams struct {
Keys, Values []string
}
// Add will append a Method parameter to the end of the route param
func (s *RouteParams) Add(key, value string) {
s.Keys = append(s.Keys, key)
s.Values = append(s.Values, value)
}
// contextKey is a value for use with context.WithValue. It's used as
// a pointer so it fits in an interface{} without allocation. This technique
type contextKey struct {
name string
}
func (k *contextKey) String() string {