You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
4.5 KiB
158 lines
4.5 KiB
3 years ago
|
package chi
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// URLParam returns the url parameter from a http.Request object.
|
||
|
func URLParam(r *http.Request, key string) string {
|
||
|
if rctx := RouteContext(r.Context()); rctx != nil {
|
||
|
return rctx.URLParam(key)
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// URLParamFromCtx returns the url parameter from a http.Request Context.
|
||
|
func URLParamFromCtx(ctx context.Context, key string) string {
|
||
|
if rctx := RouteContext(ctx); rctx != nil {
|
||
|
return rctx.URLParam(key)
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// RouteContext returns chi's routing Context object from a
|
||
|
// http.Request Context.
|
||
|
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, URL 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.
|
||
|
// See Mux#routeHTTP method.
|
||
|
RoutePath string
|
||
|
RouteMethod string
|
||
|
|
||
|
// URLParams are the stack of routeParams captured during the
|
||
|
// routing lifecycle across a stack of sub-routers.
|
||
|
URLParams 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.URLParams.Keys = x.URLParams.Keys[:0]
|
||
|
x.URLParams.Values = x.URLParams.Values[:0]
|
||
|
|
||
|
x.routePattern = ""
|
||
|
x.routeParams.Keys = x.routeParams.Keys[:0]
|
||
|
x.routeParams.Values = x.routeParams.Values[:0]
|
||
|
x.methodNotAllowed = false
|
||
|
x.parentCtx = nil
|
||
|
}
|
||
|
|
||
|
// URLParam returns the corresponding URL parameter value from the request
|
||
|
// routing context.
|
||
|
func (x *Context) URLParam(key string) string {
|
||
|
for k := len(x.URLParams.Keys) - 1; k >= 0; k-- {
|
||
|
if x.URLParams.Keys[k] == key {
|
||
|
return x.URLParams.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 http.Handler) http.Handler {
|
||
|
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
// next.ServeHTTP(w, r)
|
||
|
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
|
||
|
// measure(w, r, routePattern)
|
||
|
// })
|
||
|
// }
|
||
|
func (x *Context) RoutePattern() string {
|
||
|
routePattern := strings.Join(x.RoutePatterns, "")
|
||
|
return replaceWildcards(routePattern)
|
||
|
}
|
||
|
|
||
|
// replaceWildcards takes a route pattern and recursively replaces all
|
||
|
// occurrences of "/*/" to "/".
|
||
|
func replaceWildcards(p string) string {
|
||
|
if strings.Contains(p, "/*/") {
|
||
|
return replaceWildcards(strings.Replace(p, "/*/", "/", -1))
|
||
|
}
|
||
|
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
// RouteParams is a structure to track URL routing parameters efficiently.
|
||
|
type RouteParams struct {
|
||
|
Keys, Values []string
|
||
|
}
|
||
|
|
||
|
// Add will append a URL 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
|
||
|
// for defining context keys was copied from Go 1.7's new use of context in net/http.
|
||
|
type contextKey struct {
|
||
|
name string
|
||
|
}
|
||
|
|
||
|
func (k *contextKey) String() string {
|
||
|
return "chi context value " + k.name
|
||
|
}
|