entity_accessors.go 5.2 KB

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