entity_accessors.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package restful
  2. // Copyright 2015 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. "encoding/json"
  7. "encoding/xml"
  8. "strings"
  9. "sync"
  10. )
  11. // EntityReaderWriter can read and write values using an encoding such as JSON,XML.
  12. type EntityReaderWriter interface {
  13. // Read a serialized version of the value from the request.
  14. // The Request may have a decompressing reader. Depends on Content-Encoding.
  15. Read(req *Request, v interface{}) error
  16. // Write a serialized version of the value on the response.
  17. // The Response may have a compressing writer. Depends on Accept-Encoding.
  18. // status should be a valid Http Status code
  19. Write(resp *Response, status int, v interface{}) error
  20. }
  21. // entityAccessRegistry is a singleton
  22. var entityAccessRegistry = &entityReaderWriters{
  23. protection: new(sync.RWMutex),
  24. accessors: map[string]EntityReaderWriter{},
  25. }
  26. // entityReaderWriters associates MIME to an EntityReaderWriter
  27. type entityReaderWriters struct {
  28. protection *sync.RWMutex
  29. accessors map[string]EntityReaderWriter
  30. }
  31. func init() {
  32. RegisterEntityAccessor(MIME_JSON, NewEntityAccessorJSON(MIME_JSON))
  33. RegisterEntityAccessor(MIME_XML, NewEntityAccessorXML(MIME_XML))
  34. }
  35. // RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type.
  36. func RegisterEntityAccessor(mime string, erw EntityReaderWriter) {
  37. entityAccessRegistry.protection.Lock()
  38. defer entityAccessRegistry.protection.Unlock()
  39. entityAccessRegistry.accessors[mime] = erw
  40. }
  41. // NewEntityAccessorJSON returns a new EntityReaderWriter for accessing JSON content.
  42. // This package is already initialized with such an accessor using the MIME_JSON contentType.
  43. func NewEntityAccessorJSON(contentType string) EntityReaderWriter {
  44. return entityJSONAccess{ContentType: contentType}
  45. }
  46. // NewEntityAccessorXML returns a new EntityReaderWriter for accessing XML content.
  47. // This package is already initialized with such an accessor using the MIME_XML contentType.
  48. func NewEntityAccessorXML(contentType string) EntityReaderWriter {
  49. return entityXMLAccess{ContentType: contentType}
  50. }
  51. // accessorAt returns the registered ReaderWriter for this MIME type.
  52. func (r *entityReaderWriters) accessorAt(mime string) (EntityReaderWriter, bool) {
  53. r.protection.RLock()
  54. defer r.protection.RUnlock()
  55. er, ok := r.accessors[mime]
  56. if !ok {
  57. // retry with reverse lookup
  58. // more expensive but we are in an exceptional situation anyway
  59. for k, v := range r.accessors {
  60. if strings.Contains(mime, k) {
  61. return v, true
  62. }
  63. }
  64. }
  65. return er, ok
  66. }
  67. // entityXMLAccess is a EntityReaderWriter for XML encoding
  68. type entityXMLAccess struct {
  69. // This is used for setting the Content-Type header when writing
  70. ContentType string
  71. }
  72. // Read unmarshalls the value from XML
  73. func (e entityXMLAccess) Read(req *Request, v interface{}) error {
  74. return xml.NewDecoder(req.Request.Body).Decode(v)
  75. }
  76. // Write marshalls the value to JSON and set the Content-Type Header.
  77. func (e entityXMLAccess) Write(resp *Response, status int, v interface{}) error {
  78. return writeXML(resp, status, e.ContentType, v)
  79. }
  80. // writeXML marshalls the value to JSON and set the Content-Type Header.
  81. func writeXML(resp *Response, status int, contentType string, v interface{}) error {
  82. if v == nil {
  83. resp.WriteHeader(status)
  84. // do not write a nil representation
  85. return nil
  86. }
  87. if resp.prettyPrint {
  88. // pretty output must be created and written explicitly
  89. output, err := xml.MarshalIndent(v, " ", " ")
  90. if err != nil {
  91. return err
  92. }
  93. resp.Header().Set(HEADER_ContentType, contentType)
  94. resp.WriteHeader(status)
  95. _, err = resp.Write([]byte(xml.Header))
  96. if err != nil {
  97. return err
  98. }
  99. _, err = resp.Write(output)
  100. return err
  101. }
  102. // not-so-pretty
  103. resp.Header().Set(HEADER_ContentType, contentType)
  104. resp.WriteHeader(status)
  105. return xml.NewEncoder(resp).Encode(v)
  106. }
  107. // entityJSONAccess is a EntityReaderWriter for JSON encoding
  108. type entityJSONAccess struct {
  109. // This is used for setting the Content-Type header when writing
  110. ContentType string
  111. }
  112. // Read unmarshalls the value from JSON
  113. func (e entityJSONAccess) Read(req *Request, v interface{}) error {
  114. decoder := json.NewDecoder(req.Request.Body)
  115. decoder.UseNumber()
  116. return decoder.Decode(v)
  117. }
  118. // Write marshalls the value to JSON and set the Content-Type Header.
  119. func (e entityJSONAccess) Write(resp *Response, status int, v interface{}) error {
  120. return writeJSON(resp, status, e.ContentType, v)
  121. }
  122. // write marshalls the value to JSON and set the Content-Type Header.
  123. func writeJSON(resp *Response, status int, contentType string, v interface{}) error {
  124. if v == nil {
  125. resp.WriteHeader(status)
  126. // do not write a nil representation
  127. return nil
  128. }
  129. if resp.prettyPrint {
  130. // pretty output must be created and written explicitly
  131. output, err := json.MarshalIndent(v, " ", " ")
  132. if err != nil {
  133. return err
  134. }
  135. resp.Header().Set(HEADER_ContentType, contentType)
  136. resp.WriteHeader(status)
  137. _, err = resp.Write(output)
  138. return err
  139. }
  140. // not-so-pretty
  141. resp.Header().Set(HEADER_ContentType, contentType)
  142. resp.WriteHeader(status)
  143. return json.NewEncoder(resp).Encode(v)
  144. }