123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- package restful
- // Copyright 2015 Ernest Micklei. All rights reserved.
- // Use of this source code is governed by a license
- // that can be found in the LICENSE file.
- import (
- "encoding/json"
- "encoding/xml"
- "strings"
- "sync"
- )
- // EntityReaderWriter can read and write values using an encoding such as JSON,XML.
- type EntityReaderWriter interface {
- // Read a serialized version of the value from the request.
- // The Request may have a decompressing reader. Depends on Content-Encoding.
- Read(req *Request, v interface{}) error
- // Write a serialized version of the value on the response.
- // The Response may have a compressing writer. Depends on Accept-Encoding.
- // status should be a valid Http Status code
- Write(resp *Response, status int, v interface{}) error
- }
- // entityAccessRegistry is a singleton
- var entityAccessRegistry = &entityReaderWriters{
- protection: new(sync.RWMutex),
- accessors: map[string]EntityReaderWriter{},
- }
- // entityReaderWriters associates MIME to an EntityReaderWriter
- type entityReaderWriters struct {
- protection *sync.RWMutex
- accessors map[string]EntityReaderWriter
- }
- func init() {
- RegisterEntityAccessor(MIME_JSON, NewEntityAccessorJSON(MIME_JSON))
- RegisterEntityAccessor(MIME_XML, NewEntityAccessorXML(MIME_XML))
- }
- // RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type.
- func RegisterEntityAccessor(mime string, erw EntityReaderWriter) {
- entityAccessRegistry.protection.Lock()
- defer entityAccessRegistry.protection.Unlock()
- entityAccessRegistry.accessors[mime] = erw
- }
- // NewEntityAccessorJSON returns a new EntityReaderWriter for accessing JSON content.
- // This package is already initialized with such an accessor using the MIME_JSON contentType.
- func NewEntityAccessorJSON(contentType string) EntityReaderWriter {
- return entityJSONAccess{ContentType: contentType}
- }
- // NewEntityAccessorXML returns a new EntityReaderWriter for accessing XML content.
- // This package is already initialized with such an accessor using the MIME_XML contentType.
- func NewEntityAccessorXML(contentType string) EntityReaderWriter {
- return entityXMLAccess{ContentType: contentType}
- }
- // accessorAt returns the registered ReaderWriter for this MIME type.
- func (r *entityReaderWriters) accessorAt(mime string) (EntityReaderWriter, bool) {
- r.protection.RLock()
- defer r.protection.RUnlock()
- er, ok := r.accessors[mime]
- if !ok {
- // retry with reverse lookup
- // more expensive but we are in an exceptional situation anyway
- for k, v := range r.accessors {
- if strings.Contains(mime, k) {
- return v, true
- }
- }
- }
- return er, ok
- }
- // entityXMLAccess is a EntityReaderWriter for XML encoding
- type entityXMLAccess struct {
- // This is used for setting the Content-Type header when writing
- ContentType string
- }
- // Read unmarshalls the value from XML
- func (e entityXMLAccess) Read(req *Request, v interface{}) error {
- return xml.NewDecoder(req.Request.Body).Decode(v)
- }
- // Write marshalls the value to JSON and set the Content-Type Header.
- func (e entityXMLAccess) Write(resp *Response, status int, v interface{}) error {
- return writeXML(resp, status, e.ContentType, v)
- }
- // writeXML marshalls the value to JSON and set the Content-Type Header.
- func writeXML(resp *Response, status int, contentType string, v interface{}) error {
- if v == nil {
- resp.WriteHeader(status)
- // do not write a nil representation
- return nil
- }
- if resp.prettyPrint {
- // pretty output must be created and written explicitly
- output, err := xml.MarshalIndent(v, " ", " ")
- if err != nil {
- return err
- }
- resp.Header().Set(HEADER_ContentType, contentType)
- resp.WriteHeader(status)
- _, err = resp.Write([]byte(xml.Header))
- if err != nil {
- return err
- }
- _, err = resp.Write(output)
- return err
- }
- // not-so-pretty
- resp.Header().Set(HEADER_ContentType, contentType)
- resp.WriteHeader(status)
- return xml.NewEncoder(resp).Encode(v)
- }
- // entityJSONAccess is a EntityReaderWriter for JSON encoding
- type entityJSONAccess struct {
- // This is used for setting the Content-Type header when writing
- ContentType string
- }
- // Read unmarshalls the value from JSON
- func (e entityJSONAccess) Read(req *Request, v interface{}) error {
- decoder := json.NewDecoder(req.Request.Body)
- decoder.UseNumber()
- return decoder.Decode(v)
- }
- // Write marshalls the value to JSON and set the Content-Type Header.
- func (e entityJSONAccess) Write(resp *Response, status int, v interface{}) error {
- return writeJSON(resp, status, e.ContentType, v)
- }
- // write marshalls the value to JSON and set the Content-Type Header.
- func writeJSON(resp *Response, status int, contentType string, v interface{}) error {
- if v == nil {
- resp.WriteHeader(status)
- // do not write a nil representation
- return nil
- }
- if resp.prettyPrint {
- // pretty output must be created and written explicitly
- output, err := json.MarshalIndent(v, " ", " ")
- if err != nil {
- return err
- }
- resp.Header().Set(HEADER_ContentType, contentType)
- resp.WriteHeader(status)
- _, err = resp.Write(output)
- return err
- }
- // not-so-pretty
- resp.Header().Set(HEADER_ContentType, contentType)
- resp.WriteHeader(status)
- return json.NewEncoder(resp).Encode(v)
- }
|