Submit clojure data to reitit multipart API

(Question also asked on Slack #reitit channel, but asking here so I can easily post code)

I am trying to figure out how to send a Clojure data structure as part of a :multipart file upload to a Reitit route. The code below works, but I can only submit native values (like a string or number) as the :foo parameter and use simple predicates like string? or int?.

If I try {:bar string?} as spec for the :foo parameter and submit (clj->js {:bar "baz"}) as "foo" key, I get the exception "java.lang.ClassCastException: class java.lang.Character cannot be cast to class java.util.Map$Entry (java.lang.Character and java.util.Map$Entry are in module java.base of loader 'bootstrap')", full stack trace below.

Is there a way to get this working?

;; Reitit route
["/api5/test-upload"
        {:name :test-upload
         :coercion reitit.coercion.malli/coercion
         :post {:summary "upload a file"
                :parameters {:multipart {:foo string? ;;  Works, but cannot be {:foo {:bar string?}}
                                         :file {:filename     string?
                                                :content-type string?
                                                :size         int?
                                                :tempfile     [:fn (partial instance? File)]}}}
                :responses {200 {:body {:name string?, :size int? :foo any?}}}
                :handler (fn [{{{:keys [file foo]} :multipart} :parameters}] 
                           {:status 200
                            :body   {:name (:filename file)
                                     :size (:size file)
                                     :foo foo}})}}]
;; Clojurescript
(rf/reg-event-fx
  :upload-file
  (fn upload-image [_ [_ {:keys [body] :as something}]] 
    {:http-xhrio {:method          :post
                  :uri             "/api5/test-upload"
                  :timeout         8000
                  :body            body
                  :response-format (transit/transit-response-format)
                  :on-success      [:on-success]
                  :on-failure      [:on-failure]}}))

(defn upload-file 
  []
  (let [el            (.getElementById js/document "file-input")
        file          (aget (.-files el) 0)
        form-data     (js/FormData.)
        _             (.append form-data "file" file)
        _ (.append form-data "foo" "bar") ;; Works, but cannot be replace "bar" with for example (clj->js {:bar "baz"})
        submit-params {:body form-data}]
    (rf/dispatch [:upload-file submit-params])))

Exception when trying to submit a map

java.lang.Thread.run                            Thread.java:  833
                                  org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run             EnhancedQueueExecutor.java: 1449
                            org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask             EnhancedQueueExecutor.java: 1558
                                         org.jboss.threads.EnhancedQueueExecutor.safeRun             EnhancedQueueExecutor.java: 2019
                                  org.jboss.threads.ContextClassLoaderSavingRunnable.run  ContextClassLoaderSavingRunnable.java:   35
                                             io.undertow.server.HttpServerExchange$1.run                HttpServerExchange.java:  841
                                        io.undertow.server.Connectors.executeRootHandler                        Connectors.java:  387
                       io.undertow.server.session.SessionAttachmentHandler.handleRequest          SessionAttachmentHandler.java:   68
org.projectodd.wunderboss.web.undertow.async.websocket.UndertowWebsocket$2.handleRequest                 UndertowWebsocket.java:  109
                  immutant.web.internal.undertow/create-http-handler/reify/handleRequest                           undertow.clj:  239
                                                      bass4.db-update.core/updates-mw/fn                               core.clj:  115
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                             bass4.middleware.request-logger/wrap-logger                     request_logger.clj:   80
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                         bass4.middleware.errors/catch-internal-error-mw                             errors.clj:   34
                                             bass4.middleware.errors/catch-request-error                             errors.clj:   22
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                               bass4.embedded.middleware/embedded-iframe                         middleware.clj:  147
                                              ring.middleware.x-headers/wrap-x-header/fn                          x_headers.clj:   22 (repeats 3 times)
                                       ring.middleware.not-modified/wrap-not-modified/fn                       not_modified.clj:   61
                                 ring.middleware.default-charset/wrap-default-charset/fn                    default_charset.clj:   31
                                       ring.middleware.content-type/wrap-content-type/fn                       content_type.clj:   34
                              ring.middleware.resource/wrap-resource-prefer-resources/fn                           resource.clj:   25
                           ring.middleware.absolute-redirects/wrap-absolute-redirects/fn                 absolute_redirects.clj:   47
                                                 ring.middleware.cookies/wrap-cookies/fn                            cookies.clj:  214
                                                   ring.middleware.params/wrap-params/fn                             params.clj:   75
                               ring.middleware.multipart-params/wrap-multipart-params/fn                   multipart_params.clj:  171
                                     ring.middleware.nested-params/wrap-nested-params/fn                      nested_params.clj:   89
                                   ring.middleware.keyword-params/wrap-keyword-params/fn                     keyword_params.clj:   53
                                                 ring.middleware.webjars/wrap-webjars/fn                            webjars.clj:   41
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                               bass4.middleware.core/security-headers-mw                               core.clj:   80
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                                   bass4.middleware.lockdown/lockdown-mw                           lockdown.clj:   36
                                               bass4.middleware.core/no-cache-headers/fn                               core.clj:   71
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                                           bass4.db.middleware/inject-db                         middleware.clj:   43
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                                      bass4.middleware.file-php/File-php                           file_php.clj:   56
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                               bass4.middleware.lockdown/sms-lockdown-mw                           lockdown.clj:   55
                                                     bass4.middleware.core/wrap-mw-fn/fn                               core.clj:   67
                                                                                     ...                                             
                                       bass4.services.attack-detector/attack-detector-mw                    attack_detector.clj:  271
                                      bass4.embedded.middleware/wrap-embedded-request/fn                         middleware.clj:  142
                                                      bass4.route-rules/wrap-route-mw/fn                        route_rules.clj:  112 (repeats 2 times)
                    bass4.middleware.core/embedded-admin-routes/match-embedded-routes/fn                               core.clj:  157
                                                 bass4.session.storage/inject-session/fn                            storage.clj:  104
                                                 ring.middleware.session/wrap-session/fn                            session.clj:  108
                                                bass4.env/wrap-prone-debug-exceptions/fn                                env.clj:   18
                                                  bass4.env/wrap-session-modification/fn                                env.clj:   34
                                                  bass4.env/session-modification-wrapper                                env.clj:   29
                                                    selmer.middleware/wrap-error-page/fn                         middleware.clj:   18
                                                   ring.middleware.reload/wrap-reload/fn                             reload.clj:   39
                                    bass4.session.timeout/inject-participant-timeouts/fn                            timeout.clj:  224
                                      bass4.session.timeout/inject-participant-timeouts*                            timeout.clj:  211
                                      bass4.session.timeout/wrap-session-hard-timeout/fn                            timeout.clj:  202
                                        bass4.session.timeout/wrap-session-hard-timeout*                            timeout.clj:  197
                                          bass4.session.timeout/no-hard-timeout-response                            timeout.clj:  184
                                                        bass4.api.core/api5-routes-mw/fn                               core.clj:  124
                                                                                     ...                                             
                                                             reitit.ring/ring-handler/fn                              ring.cljc:  329
                                                   ring.middleware.params/wrap-params/fn                             params.clj:   75
                                            muuntaja.middleware/wrap-format-negotiate/fn                         middleware.clj:   96
                                             muuntaja.middleware/wrap-format-response/fn                         middleware.clj:  132
                                              muuntaja.middleware/wrap-format-request/fn                         middleware.clj:  114
                                                           reitit.ring.coercion/fn/fn/fn                          coercion.cljc:   64
                                       reitit.ring.middleware.multipart/compile/fn/fn/fn                          multipart.clj:   45
                                        reitit.ring.middleware.multipart/coerced-request                          multipart.clj:   28
                                                          reitit.coercion/coerce-request                          coercion.cljc:  111
                                                                  clojure.core/reduce-kv                               core.clj: 6919
                                                             clojure.core.protocols/fn/G                          protocols.clj:  175
                                                                         clojure.core/fn                               core.clj: 6908
                                                                                     ...                                             
                                                       reitit.coercion/coerce-request/fn                          coercion.cljc:  113
                                                      reitit.coercion/request-coercer/fn                          coercion.cljc:   84
                                                       reitit.coercion.malli/-coercer/fn                             malli.cljc:   60
                                  reitit.coercion.malli/-coercer/->coercer/reify/-decode                             malli.cljc:   46
                                                                     malli.core/-comp/fn                              core.cljc:  177
                                                                    malli.core/-guard/fn                              core.cljc:  149
                                                                     malli.core/-comp/fn                              core.cljc:  177
                                        malli.core/-map-transformer/child-transformer/fn                              core.cljc:  518
                                      malli.transform/strip-extra-keys-transformer/fn/fn                         transform.cljc:  368
                                                                  clojure.core/reduce-kv                               core.clj: 6919
                                                             clojure.core.protocols/fn/G                          protocols.clj:  175
                                                                         clojure.core/fn                               core.clj: 6888
                                                                     clojure.core/reduce                               core.clj: 6886
                                                             clojure.core.protocols/fn/G                          protocols.clj:   13
                                                               clojure.core.protocols/fn                          protocols.clj:   75
                                                       clojure.core.protocols/seq-reduce                          protocols.clj:   31
                                                             clojure.core.protocols/fn/G                          protocols.clj:   19
                                                               clojure.core.protocols/fn                          protocols.clj:  153
                                                                      clojure.core/fn/fn                               core.clj: 6899
java.lang.ClassCastException: class java.lang.Character cannot be cast to class java.util.Map$Entry (java.lang.Character and java.util.Map$Entry are in module java.base of loader 'bootstrap')

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.