context.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package tango
  2. import (
  3. "encoding/json"
  4. "encoding/xml"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "os"
  10. "path/filepath"
  11. "reflect"
  12. )
  13. type Handler interface {
  14. Handle(*Context)
  15. }
  16. type Context struct {
  17. tan *Tango
  18. Logger
  19. idx int
  20. req *http.Request
  21. ResponseWriter
  22. route *Route
  23. params url.Values
  24. callArgs []reflect.Value
  25. matched bool
  26. cookies *cookies
  27. secCookies *secureCookies
  28. action interface{}
  29. Result interface{}
  30. }
  31. func NewContext(
  32. tan *Tango,
  33. req *http.Request,
  34. resp ResponseWriter,
  35. logger Logger) *Context {
  36. return &Context{
  37. tan: tan,
  38. idx: 0,
  39. req: req,
  40. ResponseWriter: resp,
  41. Logger: logger,
  42. }
  43. }
  44. func (ctx *Context) HandleError() {
  45. ctx.tan.ErrHandler.Handle(ctx)
  46. }
  47. func (ctx *Context) Req() *http.Request {
  48. return ctx.req
  49. }
  50. func (ctx *Context) SecureCookies(secret string) Cookies {
  51. if ctx.secCookies == nil {
  52. ctx.secCookies = &secureCookies{
  53. &cookies{
  54. ctx.req,
  55. ctx.ResponseWriter,
  56. },
  57. secret,
  58. }
  59. }
  60. return ctx.secCookies
  61. }
  62. func (ctx *Context) Cookies() Cookies {
  63. if ctx.cookies == nil {
  64. ctx.cookies = &cookies{
  65. ctx.req,
  66. ctx.ResponseWriter,
  67. }
  68. }
  69. return ctx.cookies
  70. }
  71. func (ctx *Context) Route() *Route {
  72. ctx.newAction()
  73. return ctx.route
  74. }
  75. func (ctx *Context) Params() url.Values {
  76. ctx.newAction()
  77. return ctx.params
  78. }
  79. func (ctx *Context) Action() interface{} {
  80. ctx.newAction()
  81. return ctx.action
  82. }
  83. func (ctx *Context) newAction() {
  84. if !ctx.matched {
  85. reqPath := removeStick(ctx.Req().URL.Path)
  86. ctx.route, ctx.params = ctx.tan.Match(reqPath, ctx.Req().Method)
  87. if ctx.route != nil {
  88. vc := ctx.route.newAction()
  89. ctx.action = vc.Interface()
  90. switch ctx.route.routeType {
  91. case StructPtrRoute:
  92. ctx.callArgs = []reflect.Value{vc.Elem()}
  93. case StructRoute:
  94. ctx.callArgs = []reflect.Value{vc}
  95. case FuncRoute:
  96. ctx.callArgs = []reflect.Value{}
  97. case FuncHttpRoute:
  98. ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx.ResponseWriter),
  99. reflect.ValueOf(ctx.Req())}
  100. case FuncReqRoute:
  101. ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx.Req())}
  102. case FuncResponseRoute:
  103. ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx.ResponseWriter)}
  104. case FuncCtxRoute:
  105. ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx)}
  106. default:
  107. panic("routeType error")
  108. }
  109. }
  110. ctx.matched = true
  111. }
  112. }
  113. func (ctx *Context) Next() {
  114. ctx.idx += 1
  115. ctx.Invoke()
  116. }
  117. func (ctx *Context) Invoke() {
  118. if ctx.idx < len(ctx.tan.handlers) {
  119. ctx.tan.handlers[ctx.idx].Handle(ctx)
  120. } else {
  121. ctx.newAction()
  122. // route is matched
  123. if ctx.action != nil {
  124. ret := ctx.route.method.Call(ctx.callArgs)
  125. if len(ret) > 0 {
  126. ctx.Result = ret[0].Interface()
  127. }
  128. // not route matched
  129. } else {
  130. if !ctx.Written() {
  131. ctx.NotFound()
  132. }
  133. }
  134. }
  135. }
  136. func (ctx *Context) ServeFile(path string) error {
  137. http.ServeFile(ctx, ctx.Req(), path)
  138. return nil
  139. }
  140. func (ctx *Context) ServeXml(obj interface{}) error {
  141. encoder := xml.NewEncoder(ctx)
  142. err := encoder.Encode(obj)
  143. if err == nil {
  144. ctx.Header().Set("Content-Type", "application/xml")
  145. }
  146. return err
  147. }
  148. func (ctx *Context) ServeJson(obj interface{}) error {
  149. encoder := json.NewEncoder(ctx)
  150. err := encoder.Encode(obj)
  151. if err == nil {
  152. ctx.Header().Set("Content-Type", "application/json")
  153. }
  154. return err
  155. }
  156. func (ctx *Context) Download(fpath string) error {
  157. f, err := os.Open(fpath)
  158. if err != nil {
  159. return err
  160. }
  161. defer f.Close()
  162. fName := filepath.Base(fpath)
  163. ctx.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%v\"", fName))
  164. _, err = io.Copy(ctx, f)
  165. return err
  166. }
  167. func (ctx *Context) Redirect(url string, status ...int) {
  168. s := http.StatusFound
  169. if len(status) > 0 {
  170. s = status[0]
  171. }
  172. http.Redirect(ctx.ResponseWriter, ctx.Req(), url, s)
  173. }
  174. // Notmodified writes a 304 HTTP response
  175. func (ctx *Context) NotModified() {
  176. ctx.WriteHeader(http.StatusNotModified)
  177. }
  178. func (ctx *Context) Unauthorized() {
  179. ctx.Abort(http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized))
  180. }
  181. // NotFound writes a 404 HTTP response
  182. func (ctx *Context) NotFound(message ...string) {
  183. if len(message) == 0 {
  184. ctx.Abort(http.StatusNotFound, http.StatusText(http.StatusNotFound))
  185. return
  186. }
  187. ctx.Abort(http.StatusNotFound, message[0])
  188. }
  189. // Abort is a helper method that sends an HTTP header and an optional
  190. // body. It is useful for returning 4xx or 5xx errors.
  191. // Once it has been called, any return value from the handler will
  192. // not be written to the response.
  193. func (ctx *Context) Abort(status int, body string) {
  194. ctx.Result = Abort(status, body)
  195. ctx.HandleError()
  196. }
  197. type Contexter interface {
  198. SetContext(*Context)
  199. }
  200. type Ctx struct {
  201. *Context
  202. }
  203. func (c *Ctx) SetContext(ctx *Context) {
  204. c.Context = ctx
  205. }
  206. func Contexts() HandlerFunc {
  207. return func(ctx *Context) {
  208. if action := ctx.Action(); action != nil {
  209. if a, ok := action.(Contexter); ok {
  210. a.SetContext(ctx)
  211. }
  212. }
  213. ctx.Next()
  214. }
  215. }