context.go 4.4 KB

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