import (
"encoding/json"
+ "fmt"
"io"
"mime"
"net/http"
return nil, httpError(http.StatusBadRequest, err)
}
params := map[string]interface{}{}
+
+ // Load parameters from req.Form, which (after
+ // req.ParseForm()) includes the query string and -- when
+ // Content-Type is application/x-www-form-urlencoded -- the
+ // request body.
for k, values := range req.Form {
+ // All of these form values arrive as strings, so we
+ // need some type-guessing to accept non-string
+ // inputs:
+ //
+ // Values for parameters that take ints (limit=1) or
+ // bools (include_trash=1) are parsed accordingly.
+ //
+ // "null" and "" are nil.
+ //
+ // Values that look like JSON objects, arrays, or
+ // strings are parsed as JSON.
+ //
+ // The rest are left as strings.
for _, v := range values {
switch {
+ case intParams[k]:
+ params[k], err = strconv.ParseInt(v, 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ case boolParams[k]:
+ params[k] = stringToBool(v)
case v == "null" || v == "":
params[k] = nil
case strings.HasPrefix(v, "["):
return nil, err
}
params[k] = j
- case k == "limit" || k == "offset":
- params[k], err = strconv.ParseInt(v, 10, 64)
- if err != nil {
- return nil, err
- }
default:
params[k] = v
}
// as foo=["bar","baz"]?
}
}
- if ct, _, err := mime.ParseMediaType(req.Header.Get("Content-Type")); err != nil && ct == "application/json" {
+
+ // Decode body as JSON if Content-Type request header is
+ // missing or application/json.
+ mt := req.Header.Get("Content-Type")
+ if ct, _, err := mime.ParseMediaType(mt); err != nil && mt != "" {
+ return nil, fmt.Errorf("error parsing media type %q: %s", mt, err)
+ } else if (ct == "application/json" || mt == "") && req.ContentLength != 0 {
jsonParams := map[string]interface{}{}
- err := json.NewDecoder(req.Body).Decode(jsonParams)
+ err := json.NewDecoder(req.Body).Decode(&jsonParams)
if err != nil {
return nil, httpError(http.StatusBadRequest, err)
}
params["attrs"] = v
delete(params, attrsKey)
}
+
+ if order, ok := params["order"].(string); ok {
+ // We must accept strings ("foo, bar desc") and arrays
+ // (["foo", "bar desc"]) because RailsAPI does.
+ // Convert to an array here before trying to unmarshal
+ // into options structs.
+ if order == "" {
+ delete(params, "order")
+ } else {
+ params["order"] = strings.Split(order, ",")
+ }
+ }
+
return params, nil
}
}
return err
}
+
+var intParams = map[string]bool{
+ "limit": true,
+ "offset": true,
+}
+
+var boolParams = map[string]bool{
+ "distinct": true,
+ "ensure_unique_name": true,
+ "include_trash": true,
+ "include_old_versions": true,
+}
+
+func stringToBool(s string) bool {
+ switch s {
+ case "", "false", "0":
+ return false
+ default:
+ return true
+ }
+}