0
votes

I am trying to integrate compojure-api (version 1.1.12) into an existing compojure-based application. While most things work, I am having an issue with request coercions on an existing REST call whose usage would be difficult to change at this point.

  • It's a POST
  • It expects parameters via multipart/form-data
  • Most parameters are optional.
  • Most parameters are simple: string or array of string.
  • One optional parameter is expected to be a JSON-encoded map.

I define the route like this:

(POST "/endpoint" request
    :multipart-params 
        [required-strings :- (describe [s/Str] "Required, an array of strings"),
         {optional-string :- (describe s/Str "An optional string") ""},
         {others :- {s/Keyword s/Any} {}}]
    ...)

This works, unless I try to pass other-parameters in a request. For example, via curl:

curl -F "required-strings=[\"Hello\"]" -F "others={\"a\":1.0}" ...

This results in an invalid request (i.e. status 400) error with the content:

{"errors":{"others":"(not (map? a-clojure.lang.PersistentVector))"}}

I'm using ring-default's site-defaults, and I haven't modified the default coercions for the compojure api. I've traced the error to compojure.api.coerce/coerce. I can see the value that the coercer is working on, and it looks like:

{:required-strings "[\"Hello\"]"
 :others "{\"a\":1.0}"}

On line 59 of coerce.clj, (coerce value) returns an error (per schema.utils/error?).

So, is it not possible to coerce a JSON-encoded multipart parameter to a Clojure map? I can define the parameter to be a string instead of a map, and do the parsing myself, but this defeats the purpose of using compojure-api and ring-swagger.

1

1 Answers

1
votes

The coercer for others expects a clojure map not a string. To make it work you have to options.

First option: add wrap-json-params middleware and do application/json request instead of multipart/form-data:

curl ... -H 'Content-Type: application/json' \
     -d $'{
  "required-strings": ["Hello"],
  "others": {"a": 1.0}
}'

Second option: add wrap-nested-params middleware and multipart/form-data request using nested param names:

curl ... -H 'Content-Type: multipart/form-data' \
     -F "required-strings[]=Hello" \
     -F "others[a]=1.0"