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.

context.go 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package chi
  2. import (
  3. "context"
  4. "net/http"
  5. "strings"
  6. )
  7. // URLParam returns the url parameter from a http.Request object.
  8. func URLParam(r *http.Request, key string) string {
  9. if rctx := RouteContext(r.Context()); rctx != nil {
  10. return rctx.URLParam(key)
  11. }
  12. return ""
  13. }
  14. // URLParamFromCtx returns the url parameter from a http.Request Context.
  15. func URLParamFromCtx(ctx context.Context, key string) string {
  16. if rctx := RouteContext(ctx); rctx != nil {
  17. return rctx.URLParam(key)
  18. }
  19. return ""
  20. }
  21. // RouteContext returns chi's routing Context object from a
  22. // http.Request Context.
  23. func RouteContext(ctx context.Context) *Context {
  24. val, _ := ctx.Value(RouteCtxKey).(*Context)
  25. return val
  26. }
  27. // NewRouteContext returns a new routing Context object.
  28. func NewRouteContext() *Context {
  29. return &Context{}
  30. }
  31. var (
  32. // RouteCtxKey is the context.Context key to store the request context.
  33. RouteCtxKey = &contextKey{"RouteContext"}
  34. )
  35. // Context is the default routing context set on the root node of a
  36. // request context to track route patterns, URL parameters and
  37. // an optional routing path.
  38. type Context struct {
  39. Routes Routes
  40. // parentCtx is the parent of this one, for using Context as a
  41. // context.Context directly. This is an optimization that saves
  42. // 1 allocation.
  43. parentCtx context.Context
  44. // Routing path/method override used during the route search.
  45. // See Mux#routeHTTP method.
  46. RoutePath string
  47. RouteMethod string
  48. // URLParams are the stack of routeParams captured during the
  49. // routing lifecycle across a stack of sub-routers.
  50. URLParams RouteParams
  51. // Route parameters matched for the current sub-router. It is
  52. // intentionally unexported so it cant be tampered.
  53. routeParams RouteParams
  54. // The endpoint routing pattern that matched the request URI path
  55. // or `RoutePath` of the current sub-router. This value will update
  56. // during the lifecycle of a request passing through a stack of
  57. // sub-routers.
  58. routePattern string
  59. // Routing pattern stack throughout the lifecycle of the request,
  60. // across all connected routers. It is a record of all matching
  61. // patterns across a stack of sub-routers.
  62. RoutePatterns []string
  63. // methodNotAllowed hint
  64. methodNotAllowed bool
  65. }
  66. // Reset a routing context to its initial state.
  67. func (x *Context) Reset() {
  68. x.Routes = nil
  69. x.RoutePath = ""
  70. x.RouteMethod = ""
  71. x.RoutePatterns = x.RoutePatterns[:0]
  72. x.URLParams.Keys = x.URLParams.Keys[:0]
  73. x.URLParams.Values = x.URLParams.Values[:0]
  74. x.routePattern = ""
  75. x.routeParams.Keys = x.routeParams.Keys[:0]
  76. x.routeParams.Values = x.routeParams.Values[:0]
  77. x.methodNotAllowed = false
  78. x.parentCtx = nil
  79. }
  80. // URLParam returns the corresponding URL parameter value from the request
  81. // routing context.
  82. func (x *Context) URLParam(key string) string {
  83. for k := len(x.URLParams.Keys) - 1; k >= 0; k-- {
  84. if x.URLParams.Keys[k] == key {
  85. return x.URLParams.Values[k]
  86. }
  87. }
  88. return ""
  89. }
  90. // RoutePattern builds the routing pattern string for the particular
  91. // request, at the particular point during routing. This means, the value
  92. // will change throughout the execution of a request in a router. That is
  93. // why its advised to only use this value after calling the next handler.
  94. //
  95. // For example,
  96. //
  97. // func Instrument(next http.Handler) http.Handler {
  98. // return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  99. // next.ServeHTTP(w, r)
  100. // routePattern := chi.RouteContext(r.Context()).RoutePattern()
  101. // measure(w, r, routePattern)
  102. // })
  103. // }
  104. func (x *Context) RoutePattern() string {
  105. routePattern := strings.Join(x.RoutePatterns, "")
  106. return replaceWildcards(routePattern)
  107. }
  108. // replaceWildcards takes a route pattern and recursively replaces all
  109. // occurrences of "/*/" to "/".
  110. func replaceWildcards(p string) string {
  111. if strings.Contains(p, "/*/") {
  112. return replaceWildcards(strings.Replace(p, "/*/", "/", -1))
  113. }
  114. return p
  115. }
  116. // RouteParams is a structure to track URL routing parameters efficiently.
  117. type RouteParams struct {
  118. Keys, Values []string
  119. }
  120. // Add will append a URL parameter to the end of the route param
  121. func (s *RouteParams) Add(key, value string) {
  122. s.Keys = append(s.Keys, key)
  123. s.Values = append(s.Values, value)
  124. }
  125. // contextKey is a value for use with context.WithValue. It's used as
  126. // a pointer so it fits in an interface{} without allocation. This technique
  127. // for defining context keys was copied from Go 1.7's new use of context in net/http.
  128. type contextKey struct {
  129. name string
  130. }
  131. func (k *contextKey) String() string {
  132. return "chi context value " + k.name
  133. }