request.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package restful
  2. // Copyright 2013 Ernest Micklei. All rights reserved.
  3. // Use of this source code is governed by a license
  4. // that can be found in the LICENSE file.
  5. import (
  6. "bytes"
  7. "compress/zlib"
  8. "io/ioutil"
  9. "net/http"
  10. )
  11. var defaultRequestContentType string
  12. var doCacheReadEntityBytes = false
  13. // Request is a wrapper for a http Request that provides convenience methods
  14. type Request struct {
  15. Request *http.Request
  16. bodyContent *[]byte // to cache the request body for multiple reads of ReadEntity
  17. pathParameters map[string]string
  18. attributes map[string]interface{} // for storing request-scoped values
  19. selectedRoutePath string // root path + route path that matched the request, e.g. /meetings/{id}/attendees
  20. }
  21. func NewRequest(httpRequest *http.Request) *Request {
  22. return &Request{
  23. Request: httpRequest,
  24. pathParameters: map[string]string{},
  25. attributes: map[string]interface{}{},
  26. } // empty parameters, attributes
  27. }
  28. // If ContentType is missing or */* is given then fall back to this type, otherwise
  29. // a "Unable to unmarshal content of type:" response is returned.
  30. // Valid values are restful.MIME_JSON and restful.MIME_XML
  31. // Example:
  32. // restful.DefaultRequestContentType(restful.MIME_JSON)
  33. func DefaultRequestContentType(mime string) {
  34. defaultRequestContentType = mime
  35. }
  36. // SetCacheReadEntity controls whether the response data ([]byte) is cached such that ReadEntity is repeatable.
  37. // Default is true (due to backwardcompatibility). For better performance, you should set it to false if you don't need it.
  38. func SetCacheReadEntity(doCache bool) {
  39. doCacheReadEntityBytes = doCache
  40. }
  41. // PathParameter accesses the Path parameter value by its name
  42. func (r *Request) PathParameter(name string) string {
  43. return r.pathParameters[name]
  44. }
  45. // PathParameters accesses the Path parameter values
  46. func (r *Request) PathParameters() map[string]string {
  47. return r.pathParameters
  48. }
  49. // QueryParameter returns the (first) Query parameter value by its name
  50. func (r *Request) QueryParameter(name string) string {
  51. return r.Request.FormValue(name)
  52. }
  53. // BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error.
  54. func (r *Request) BodyParameter(name string) (string, error) {
  55. err := r.Request.ParseForm()
  56. if err != nil {
  57. return "", err
  58. }
  59. return r.Request.PostFormValue(name), nil
  60. }
  61. // HeaderParameter returns the HTTP Header value of a Header name or empty if missing
  62. func (r *Request) HeaderParameter(name string) string {
  63. return r.Request.Header.Get(name)
  64. }
  65. // ReadEntity checks the Accept header and reads the content into the entityPointer.
  66. func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
  67. contentType := r.Request.Header.Get(HEADER_ContentType)
  68. contentEncoding := r.Request.Header.Get(HEADER_ContentEncoding)
  69. // OLD feature, cache the body for reads
  70. if doCacheReadEntityBytes {
  71. if r.bodyContent == nil {
  72. data, err := ioutil.ReadAll(r.Request.Body)
  73. if err != nil {
  74. return err
  75. }
  76. r.bodyContent = &data
  77. }
  78. r.Request.Body = ioutil.NopCloser(bytes.NewReader(*r.bodyContent))
  79. }
  80. // check if the request body needs decompression
  81. if ENCODING_GZIP == contentEncoding {
  82. gzipReader := currentCompressorProvider.AcquireGzipReader()
  83. defer currentCompressorProvider.ReleaseGzipReader(gzipReader)
  84. gzipReader.Reset(r.Request.Body)
  85. r.Request.Body = gzipReader
  86. } else if ENCODING_DEFLATE == contentEncoding {
  87. zlibReader, err := zlib.NewReader(r.Request.Body)
  88. if err != nil {
  89. return err
  90. }
  91. r.Request.Body = zlibReader
  92. }
  93. // lookup the EntityReader, use defaultRequestContentType if needed and provided
  94. entityReader, ok := entityAccessRegistry.accessorAt(contentType)
  95. if !ok {
  96. if len(defaultRequestContentType) != 0 {
  97. entityReader, ok = entityAccessRegistry.accessorAt(defaultRequestContentType)
  98. }
  99. if !ok {
  100. return NewError(http.StatusBadRequest, "Unable to unmarshal content of type:"+contentType)
  101. }
  102. }
  103. return entityReader.Read(r, entityPointer)
  104. }
  105. // SetAttribute adds or replaces the attribute with the given value.
  106. func (r *Request) SetAttribute(name string, value interface{}) {
  107. r.attributes[name] = value
  108. }
  109. // Attribute returns the value associated to the given name. Returns nil if absent.
  110. func (r Request) Attribute(name string) interface{} {
  111. return r.attributes[name]
  112. }
  113. // SelectedRoutePath root path + route path that matched the request, e.g. /meetings/{id}/attendees
  114. func (r Request) SelectedRoutePath() string {
  115. return r.selectedRoutePath
  116. }