Matthias Andreas Benkard | 832a54e | 2019-01-29 09:27:38 +0100 | [diff] [blame] | 1 | package restful |
| 2 | |
| 3 | // Copyright 2013 Ernest Micklei. All rights reserved. |
| 4 | // Use of this source code is governed by a license |
| 5 | // that can be found in the LICENSE file. |
| 6 | |
| 7 | import ( |
| 8 | "compress/zlib" |
| 9 | "net/http" |
| 10 | ) |
| 11 | |
| 12 | var defaultRequestContentType string |
| 13 | |
| 14 | // Request is a wrapper for a http Request that provides convenience methods |
| 15 | type Request struct { |
| 16 | Request *http.Request |
| 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 | |
| 22 | func NewRequest(httpRequest *http.Request) *Request { |
| 23 | return &Request{ |
| 24 | Request: httpRequest, |
| 25 | pathParameters: map[string]string{}, |
| 26 | attributes: map[string]interface{}{}, |
| 27 | } // empty parameters, attributes |
| 28 | } |
| 29 | |
| 30 | // If ContentType is missing or */* is given then fall back to this type, otherwise |
| 31 | // a "Unable to unmarshal content of type:" response is returned. |
| 32 | // Valid values are restful.MIME_JSON and restful.MIME_XML |
| 33 | // Example: |
| 34 | // restful.DefaultRequestContentType(restful.MIME_JSON) |
| 35 | func DefaultRequestContentType(mime string) { |
| 36 | defaultRequestContentType = mime |
| 37 | } |
| 38 | |
| 39 | // PathParameter accesses the Path parameter value by its name |
| 40 | func (r *Request) PathParameter(name string) string { |
| 41 | return r.pathParameters[name] |
| 42 | } |
| 43 | |
| 44 | // PathParameters accesses the Path parameter values |
| 45 | func (r *Request) PathParameters() map[string]string { |
| 46 | return r.pathParameters |
| 47 | } |
| 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 | |
| 54 | // QueryParameters returns the all the query parameters values by name |
| 55 | func (r *Request) QueryParameters(name string) []string { |
| 56 | return r.Request.URL.Query()[name] |
| 57 | } |
| 58 | |
| 59 | // 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. |
| 60 | func (r *Request) BodyParameter(name string) (string, error) { |
| 61 | err := r.Request.ParseForm() |
| 62 | if err != nil { |
| 63 | return "", err |
| 64 | } |
| 65 | return r.Request.PostFormValue(name), nil |
| 66 | } |
| 67 | |
| 68 | // HeaderParameter returns the HTTP Header value of a Header name or empty if missing |
| 69 | func (r *Request) HeaderParameter(name string) string { |
| 70 | return r.Request.Header.Get(name) |
| 71 | } |
| 72 | |
| 73 | // ReadEntity checks the Accept header and reads the content into the entityPointer. |
| 74 | func (r *Request) ReadEntity(entityPointer interface{}) (err error) { |
| 75 | contentType := r.Request.Header.Get(HEADER_ContentType) |
| 76 | contentEncoding := r.Request.Header.Get(HEADER_ContentEncoding) |
| 77 | |
| 78 | // check if the request body needs decompression |
| 79 | if ENCODING_GZIP == contentEncoding { |
| 80 | gzipReader := currentCompressorProvider.AcquireGzipReader() |
| 81 | defer currentCompressorProvider.ReleaseGzipReader(gzipReader) |
| 82 | gzipReader.Reset(r.Request.Body) |
| 83 | r.Request.Body = gzipReader |
| 84 | } else if ENCODING_DEFLATE == contentEncoding { |
| 85 | zlibReader, err := zlib.NewReader(r.Request.Body) |
| 86 | if err != nil { |
| 87 | return err |
| 88 | } |
| 89 | r.Request.Body = zlibReader |
| 90 | } |
| 91 | |
| 92 | // lookup the EntityReader, use defaultRequestContentType if needed and provided |
| 93 | entityReader, ok := entityAccessRegistry.accessorAt(contentType) |
| 94 | if !ok { |
| 95 | if len(defaultRequestContentType) != 0 { |
| 96 | entityReader, ok = entityAccessRegistry.accessorAt(defaultRequestContentType) |
| 97 | } |
| 98 | if !ok { |
| 99 | return NewError(http.StatusBadRequest, "Unable to unmarshal content of type:"+contentType) |
| 100 | } |
| 101 | } |
| 102 | return entityReader.Read(r, entityPointer) |
| 103 | } |
| 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 | |
| 110 | // Attribute returns the value associated to the given name. Returns nil if absent. |
| 111 | func (r Request) Attribute(name string) interface{} { |
| 112 | return r.attributes[name] |
| 113 | } |
| 114 | |
| 115 | // SelectedRoutePath root path + route path that matched the request, e.g. /meetings/{id}/attendees |
| 116 | func (r Request) SelectedRoutePath() string { |
| 117 | return r.selectedRoutePath |
| 118 | } |