entity_accessors.go 5.0 KB

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