No Description
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.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package phi
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. )
  7. var (
  8. RouteCtxKey = &contextKey{"RouteContext"}
  9. )
  10. // Context is the generic context interface.
  11. type Context interface {
  12. // ResponseWriter interface
  13. http.ResponseWriter
  14. // RoutePath is the routing path override used by sub routers in the context.
  15. RoutePath() string
  16. // SetRoutePath updates the route path.
  17. SetRoutePath(string)
  18. // SetResponseWriter updates the HTTP response writer.
  19. SetResponseWriter(http.ResponseWriter)
  20. // Request is the orignal HTTP request.
  21. Request() *http.Request
  22. // SetRequest updates the HTTP request.
  23. SetRequest(*http.Request)
  24. // Context cancellation.
  25. Done() <-chan struct{}
  26. // NotFound response.
  27. NotFound()
  28. // Error response.
  29. Error(err error, status ...int)
  30. // Redirect response.
  31. Redirect(location string, status ...int)
  32. // Param retrieves a named URL parameter.
  33. Param(key string) string
  34. // Value from the request context.
  35. Value(key interface{}) interface{}
  36. // SetValue in the request context.
  37. SetValue(key, value interface{})
  38. }
  39. // RoutingContext is the default routing context set on the root node of a
  40. // request context to track URL parameters and an optional routing path.
  41. type RoutingContext struct {
  42. http.ResponseWriter
  43. // URL routing parameter key and values.
  44. URLParams params
  45. // Routing path override used by subrouters.
  46. routePath string
  47. // Routing pattern matching the path.
  48. routePattern string
  49. // Routing patterns throughout the lifecycle of the request,
  50. // across all connected routers.
  51. routePatterns []string
  52. // Data set by the user.
  53. data map[string]interface{}
  54. // Request is the original http.Request.
  55. request *http.Request
  56. }
  57. // NewRoutingContext returns a new RoutingContext.
  58. func NewRoutingContext() *RoutingContext {
  59. return &RoutingContext{
  60. data: make(map[string]interface{}),
  61. }
  62. }
  63. // reset a routing context to its initial state.
  64. func (x *RoutingContext) reset(w http.ResponseWriter, r *http.Request) {
  65. r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, x))
  66. x.ResponseWriter = w
  67. x.request = r
  68. x.URLParams = x.URLParams[:0]
  69. x.routePath = ""
  70. x.routePattern = ""
  71. x.routePatterns = x.routePatterns[:0]
  72. x.data = make(map[string]interface{})
  73. }
  74. func (x *RoutingContext) RoutePath() string {
  75. return x.routePath
  76. }
  77. func (x *RoutingContext) SetRoutePath(routePath string) {
  78. x.routePath = routePath
  79. }
  80. func (x *RoutingContext) SetResponseWriter(w http.ResponseWriter) {
  81. x.ResponseWriter = w
  82. }
  83. func (x *RoutingContext) Request() *http.Request {
  84. return x.request
  85. }
  86. func (x *RoutingContext) SetRequest(r *http.Request) {
  87. x.request = r
  88. }
  89. func (x *RoutingContext) Data() map[string]interface{} {
  90. return x.data
  91. }
  92. func (x *RoutingContext) SetData(key string, value interface{}) {
  93. x.data[key] = value
  94. }
  95. // NotFound response.
  96. func (x *RoutingContext) NotFound() {
  97. x.Error(nil, 404)
  98. }
  99. // Error renders a default error page.
  100. func (x *RoutingContext) Error(err error, status ...int) {
  101. code := http.StatusInternalServerError
  102. if len(status) == 1 {
  103. code = status[0]
  104. }
  105. x.Header().Set("Content-Type", "text/plain; charset=utf-8")
  106. x.Header().Set("X-Content-Type-Options", "nosniff")
  107. x.WriteHeader(code)
  108. if err == nil {
  109. fmt.Fprint(x, http.StatusText(code))
  110. } else {
  111. fmt.Fprint(x, err.Error())
  112. }
  113. }
  114. // Redirect to a new location
  115. func (x *RoutingContext) Redirect(location string, status ...int) {
  116. code := http.StatusFound
  117. if len(status) == 1 {
  118. code = status[0]
  119. }
  120. http.Redirect(x, x.request, location, code)
  121. }
  122. // Param retrieves a named URL parameter.
  123. func (x *RoutingContext) Param(key string) string {
  124. return x.URLParams.Get(key)
  125. }
  126. /* Wrappers for http.Request.Context and friends */
  127. // Done channel for cancelation.
  128. func (x *RoutingContext) Done() <-chan struct{} {
  129. return x.request.Context().Done()
  130. }
  131. // Value retrieves a value from http.Request context.
  132. func (x *RoutingContext) Value(key interface{}) interface{} {
  133. return x.request.Context().Value(key)
  134. }
  135. // SetValue adds a key-value pair to the http.Request context.
  136. func (x *RoutingContext) SetValue(key, value interface{}) {
  137. x.request = x.request.WithContext(context.WithValue(x.request.Context(), key, value))
  138. }
  139. // RouteContext returns phi's routing Context object from a
  140. // http.Request Context.
  141. func RouteContext(ctx context.Context) Context {
  142. return ctx.Value(RouteCtxKey).(Context)
  143. }
  144. // URLParam returns the url parameter from a http.Request object.
  145. /*
  146. func URLParam(r *http.Request, key string) string {
  147. if rctx := RouteContext(r.Context()); rctx != nil {
  148. return rctx.URLParams.Get(key)
  149. }
  150. return ""
  151. }
  152. */
  153. // contextKey is a value for use with context.WithValue. It's used as
  154. // a pointer so it fits in an interface{} without allocation. This technique
  155. // for defining context keys was copied from Go 1.7's new use of context in net/http.
  156. type contextKey struct {
  157. name string
  158. }
  159. func (k *contextKey) String() string {
  160. return "phi context value " + k.name
  161. }