-
Notifications
You must be signed in to change notification settings - Fork 0
/
httpdecode.go
130 lines (118 loc) · 3.95 KB
/
httpdecode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package httpdecode
import (
"encoding/json"
"mime"
"net/http"
"github.com/go-chi/chi"
"github.com/gorilla/schema"
"github.com/shantanu-hashcash/go/support/errors"
)
// DecodePath decodes parameters from the path in a request used with the
// github.com/go-chi/chi muxing module.
func DecodePath(r *http.Request, v interface{}) error {
rctx := chi.RouteContext(r.Context())
if rctx == nil {
return nil
}
params := rctx.URLParams
paramMap := map[string][]string{}
for i, k := range params.Keys {
if i >= len(params.Values) {
break
}
v := params.Values[i]
paramMap[k] = append(paramMap[k], v)
}
dec := schema.NewDecoder()
dec.SetAliasTag("path")
dec.IgnoreUnknownKeys(true)
return dec.Decode(v, paramMap)
}
// DecodeQuery decodes the query string from r into v.
func DecodeQuery(r *http.Request, v interface{}) error {
dec := schema.NewDecoder()
dec.SetAliasTag("query")
dec.IgnoreUnknownKeys(true)
return dec.Decode(v, r.URL.Query())
}
// DecodeJSON decodes JSON request from r into v.
func DecodeJSON(r *http.Request, v interface{}) error {
dec := json.NewDecoder(r.Body)
dec.UseNumber()
return dec.Decode(v)
}
// DecodeForm decodes form URL encoded requests from r into v.
//
// The type of the value given can use `form` tags on fields in the same way as
// the `json` tag to name fields.
//
// An error will be returned if the request is not a POST, PUT, or PATCH
// request.
//
// An error will be returned if the request has a media type in the
// Content-Type not equal to application/x-www-form-urlencoded, or if the
// Content-Type header cannot be parsed.
func DecodeForm(r *http.Request, v interface{}) error {
if r.Method != "POST" && r.Method != "PUT" && r.Method != "PATCH" {
return errors.Errorf("method POST, PUT, or PATCH required for form decoding: request has method %q", r.Method)
}
contentType := r.Header.Get("Content-Type")
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil {
return errors.Wrap(err, "content type application/x-www-form-urlencoded required for form decoding")
}
if mediaType != "application/x-www-form-urlencoded" {
return errors.Errorf("content type application/x-www-form-urlencoded required for form decoding: received content type %q", mediaType)
}
err = r.ParseForm()
if err != nil {
return err
}
dec := schema.NewDecoder()
dec.SetAliasTag("form")
dec.IgnoreUnknownKeys(true)
return dec.Decode(v, r.PostForm)
}
// Decode decodes form URL encoded requests and JSON requests from r into v.
// Also decodes path (chi only) and query parameters.
//
// The requests Content-Type header informs if the request should be decoded
// using a form URL encoded decoder or using a JSON decoder.
//
// A Content-Type of application/x-www-form-urlencoded will result in form
// decoding. Any other content type will result in JSON decoding because it is
// common to make JSON requests without a Content-Type where-as correctly
// formatted form URL encoded requests are more often accompanied by the
// appropriate Content-Type.
//
// An error is returned if the Content-Type cannot be parsed by a mime
// media-type parser.
//
// See DecodePath, DecodeQuery, DecodeForm and DecodeJSON for details about
// the types of errors that may occur.
func Decode(r *http.Request, v interface{}) error {
err := DecodePath(r, v)
if err != nil {
return errors.Wrap(err, "path params could not be parsed")
}
err = DecodeQuery(r, v)
if err != nil {
return errors.Wrap(err, "query could not be parsed")
}
contentType := r.Header.Get("Content-Type")
if contentType != "" {
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil {
return errors.Wrap(err, "content type could not be parsed")
}
if mediaType == "application/x-www-form-urlencoded" {
return DecodeForm(r, v)
}
}
// A nil body means the request has no body, such as a GET request.
// Calling DecodeJSON when receiving GET requests will result in EOF.
if r.Body != nil && r.Body != http.NoBody {
return DecodeJSON(r, v)
}
return nil
}