liberator icon indicating copy to clipboard operation
liberator copied to clipboard

How to extract the form data from a request in liberator?

Open zendevil opened this issue 5 years ago • 1 comments

I have the following client-side request:

(let [form-data (doto
                       (js/FormData.)
                     (.append "filename" file))]
     (ajax-request
      {:uri "/some/uri"
       :method :post
       :body form-data
       :format (raw-request-format)
       :response-format (raw-response-format)})
     )

And on the server (with liberator):

(defresource some-resource [_]
  :allowed-methods [:get :post]
  :available-media-types ["application/json"]
  :exists? (fn [ctx]
             (prn "form-data " (-> ctx :request :body slurp)) ;prints
             (prn "form-data " (-> ctx :request :body slurp cheshire/decode)) ;doesn't print
             ))

At the server, I do get the print of the form-data like so:

"form-data " "------WebKitFormBoundaryI9CA2zKhFT0Stmx7\r\nContent-Disposition: form-data; name=\"new-name\"\r\n\r\n{:uploaded-file #object[File [object File]], :name \"somename\", :another \"1234\"}\r\n------WebKitFormBoundaryI9CA2zKhFT0Stmx7--\r\n"

But the next prn doesn't print anything where my goal is to extract the :uploaded-file. What am I doing wrong?

zendevil avatar Mar 04 '20 15:03 zendevil

I bet you should use ring.middleware.params/wrap-params to get form-data in :form-params map of request. Then, your form data will appear in this sub-map with keys corresponding to names of inputs you had specified in your HTML (I assume but omit such HTML):

(require '[ring.middleware.params :refer [wrap-params]])
(require '[liberator.core :refer [defresource]])
(require '[compojure.core :refer [defroutes ANY]])
(require '[expectations.clojure.test :refer [defexpect expect]])
(require '[ring.mock.request :as mock])

(defresource handle-form-uploads
  :allowed-methods #{:get :post}
  :available-media-types ["text/plain"]
  :handle-created (fn [ctx]
                    (str (get-in ctx [:request :form-params "greeting"]) ", "
                         (get-in ctx [:request :form-params "name"]) "!")))

(defroutes my-app-routes
  (ANY "/" [] handle-form-uploads))

(def my-app
  (wrap-params my-app-routes))

(defexpect handling-form-uploads
  (expect
    {
      :status 201
      :headers {"Content-Type" "text/plain;charset=UTF-8" "Vary" "Accept"}
      :body "Hello, Prikshet!"}
    (my-app (-> (mock/request :post "/")
                (mock/body {:name "Prikshet" :greeting "Hello"})))))

(handling-form-uploads)

deps.edn:

{
  :deps
  {
    liberator/liberator {:mvn/version "0.15.3"}
    ring/ring-core {:mvn/version "1.11.0-RC2"}
    compojure/compojure {:mvn/version "1.7.0"}
    expectations/clojure-test {:mvn/version "1.2.1"}
    ring/ring-mock {:mvn/version "0.4.0"}}}

Notice that "form" keys will appear in map as strings, not keywords.

Take a look at ring.middlewares.multipart-params if you want to process uploaded files as well.

tribals avatar Dec 21 '23 20:12 tribals