context.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. package gin
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "github.com/gin-gonic/gin/binding"
  7. "github.com/gin-gonic/gin/render"
  8. "github.com/julienschmidt/httprouter"
  9. "log"
  10. "net/http"
  11. )
  12. const (
  13. ErrorTypeInternal = 1 << iota
  14. ErrorTypeExternal = 1 << iota
  15. ErrorTypeAll = 0xffffffff
  16. )
  17. // Used internally to collect errors that occurred during an http request.
  18. type errorMsg struct {
  19. Err string `json:"error"`
  20. Type uint32 `json:"-"`
  21. Meta interface{} `json:"meta"`
  22. }
  23. type errorMsgs []errorMsg
  24. func (a errorMsgs) ByType(typ uint32) errorMsgs {
  25. if len(a) == 0 {
  26. return a
  27. }
  28. result := make(errorMsgs, 0, len(a))
  29. for _, msg := range a {
  30. if msg.Type&typ > 0 {
  31. result = append(result, msg)
  32. }
  33. }
  34. return result
  35. }
  36. func (a errorMsgs) String() string {
  37. if len(a) == 0 {
  38. return ""
  39. }
  40. var buffer bytes.Buffer
  41. for i, msg := range a {
  42. text := fmt.Sprintf("Error #%02d: %s \n Meta: %v\n", (i + 1), msg.Err, msg.Meta)
  43. buffer.WriteString(text)
  44. }
  45. return buffer.String()
  46. }
  47. // Context is the most important part of gin. It allows us to pass variables between middleware,
  48. // manage the flow, validate the JSON of a request and render a JSON response for example.
  49. type Context struct {
  50. Request *http.Request
  51. Writer ResponseWriter
  52. Keys map[string]interface{}
  53. Errors errorMsgs
  54. Params httprouter.Params
  55. Engine *Engine
  56. handlers []HandlerFunc
  57. index int8
  58. }
  59. /************************************/
  60. /********** ROUTES GROUPING *********/
  61. /************************************/
  62. func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context {
  63. c := engine.cache.Get().(*Context)
  64. c.Writer.reset(w)
  65. c.Request = req
  66. c.Params = params
  67. c.handlers = handlers
  68. c.Keys = nil
  69. c.index = -1
  70. return c
  71. }
  72. /************************************/
  73. /****** FLOW AND ERROR MANAGEMENT****/
  74. /************************************/
  75. func (c *Context) Copy() *Context {
  76. var cp Context = *c
  77. cp.index = AbortIndex
  78. cp.handlers = nil
  79. return &cp
  80. }
  81. // Next should be used only in the middlewares.
  82. // It executes the pending handlers in the chain inside the calling handler.
  83. // See example in github.
  84. func (c *Context) Next() {
  85. c.index++
  86. s := int8(len(c.handlers))
  87. for ; c.index < s; c.index++ {
  88. c.handlers[c.index](c)
  89. }
  90. }
  91. // Forces the system to do not continue calling the pending handlers.
  92. // For example, the first handler checks if the request is authorized. If it's not, context.Abort(401) should be called.
  93. // The rest of pending handlers would never be called for that request.
  94. func (c *Context) Abort(code int) {
  95. if code >= 0 {
  96. c.Writer.WriteHeader(code)
  97. }
  98. c.index = AbortIndex
  99. }
  100. // Fail is the same as Abort plus an error message.
  101. // Calling `context.Fail(500, err)` is equivalent to:
  102. // ```
  103. // context.Error("Operation aborted", err)
  104. // context.Abort(500)
  105. // ```
  106. func (c *Context) Fail(code int, err error) {
  107. c.Error(err, "Operation aborted")
  108. c.Abort(code)
  109. }
  110. func (c *Context) ErrorTyped(err error, typ uint32, meta interface{}) {
  111. c.Errors = append(c.Errors, errorMsg{
  112. Err: err.Error(),
  113. Type: typ,
  114. Meta: meta,
  115. })
  116. }
  117. // Attaches an error to the current context. The error is pushed to a list of errors.
  118. // It's a good idea to call Error for each error that occurred during the resolution of a request.
  119. // A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
  120. func (c *Context) Error(err error, meta interface{}) {
  121. c.ErrorTyped(err, ErrorTypeExternal, meta)
  122. }
  123. func (c *Context) LastError() error {
  124. s := len(c.Errors)
  125. if s > 0 {
  126. return errors.New(c.Errors[s-1].Err)
  127. } else {
  128. return nil
  129. }
  130. }
  131. /************************************/
  132. /******** METADATA MANAGEMENT********/
  133. /************************************/
  134. // Sets a new pair key/value just for the specified context.
  135. // It also lazy initializes the hashmap.
  136. func (c *Context) Set(key string, item interface{}) {
  137. if c.Keys == nil {
  138. c.Keys = make(map[string]interface{})
  139. }
  140. c.Keys[key] = item
  141. }
  142. // Get returns the value for the given key or an error if the key does not exist.
  143. func (c *Context) Get(key string) (interface{}, error) {
  144. if c.Keys != nil {
  145. item, ok := c.Keys[key]
  146. if ok {
  147. return item, nil
  148. }
  149. }
  150. return nil, errors.New("Key does not exist.")
  151. }
  152. // MustGet returns the value for the given key or panics if the value doesn't exist.
  153. func (c *Context) MustGet(key string) interface{} {
  154. value, err := c.Get(key)
  155. if err != nil || value == nil {
  156. log.Panicf("Key %s doesn't exist", key)
  157. }
  158. return value
  159. }
  160. /************************************/
  161. /******** ENCOGING MANAGEMENT********/
  162. /************************************/
  163. // This function checks the Content-Type to select a binding engine automatically,
  164. // Depending the "Content-Type" header different bindings are used:
  165. // "application/json" --> JSON binding
  166. // "application/xml" --> XML binding
  167. // else --> returns an error
  168. // if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
  169. func (c *Context) Bind(obj interface{}) bool {
  170. var b binding.Binding
  171. ctype := filterFlags(c.Request.Header.Get("Content-Type"))
  172. switch {
  173. case c.Request.Method == "GET" || ctype == MIMEPOSTForm:
  174. b = binding.Form
  175. case ctype == MIMEJSON:
  176. b = binding.JSON
  177. case ctype == MIMEXML || ctype == MIMEXML2:
  178. b = binding.XML
  179. default:
  180. c.Fail(400, errors.New("unknown content-type: "+ctype))
  181. return false
  182. }
  183. return c.BindWith(obj, b)
  184. }
  185. func (c *Context) BindWith(obj interface{}, b binding.Binding) bool {
  186. if err := b.Bind(c.Request, obj); err != nil {
  187. c.Fail(400, err)
  188. return false
  189. }
  190. return true
  191. }
  192. func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
  193. if err := render.Render(c.Writer, code, obj...); err != nil {
  194. c.ErrorTyped(err, ErrorTypeInternal, obj)
  195. c.Abort(500)
  196. }
  197. }
  198. // Serializes the given struct as JSON into the response body in a fast and efficient way.
  199. // It also sets the Content-Type as "application/json".
  200. func (c *Context) JSON(code int, obj interface{}) {
  201. c.Render(code, render.JSON, obj)
  202. }
  203. // Serializes the given struct as XML into the response body in a fast and efficient way.
  204. // It also sets the Content-Type as "application/xml".
  205. func (c *Context) XML(code int, obj interface{}) {
  206. c.Render(code, render.XML, obj)
  207. }
  208. // Renders the HTTP template specified by its file name.
  209. // It also updates the HTTP code and sets the Content-Type as "text/html".
  210. // See http://golang.org/doc/articles/wiki/
  211. func (c *Context) HTML(code int, name string, obj interface{}) {
  212. c.Render(code, c.Engine.HTMLRender, name, obj)
  213. }
  214. // Writes the given string into the response body and sets the Content-Type to "text/plain".
  215. func (c *Context) String(code int, format string, values ...interface{}) {
  216. c.Render(code, render.Plain, format, values)
  217. }
  218. // Writes some data into the body stream and updates the HTTP code.
  219. func (c *Context) Data(code int, contentType string, data []byte) {
  220. if len(contentType) > 0 {
  221. c.Writer.Header().Set("Content-Type", contentType)
  222. }
  223. if code >= 0 {
  224. c.Writer.WriteHeader(code)
  225. }
  226. c.Writer.Write(data)
  227. }
  228. // Writes the specified file into the body stream
  229. func (c *Context) File(filepath string) {
  230. http.ServeFile(c.Writer, c.Request, filepath)
  231. }