2

I'm using Liberator, and am having a hard time getting my POSTed data into a map using with keywords as the keys. Here is my resource, with a few printlines for testing:

(defresource finish_validation
             :allowed-methods [:post]
             :available-media-types ["application/json"]
             :post! (fn [context]
                      (let [params (slurp (get-in context [:request :body]))
                            mapped_params (cheshire/parse-string params)]

                        (println (type params))
                        (println (type mapped_params))
                        (validation/finish mapped_params)))
             :handle-created (println ))

For testing, I'm posting the data using curl:

curl -H "Content-Type: application/json" -X POST -d '{"email":"test@foo.com","code":"xyz"}' http://localhost:8080/validate

cheshire converts the params into a map, but the keys are not keywords: I get {email test@foo.com, code xyz} as the output, instead of the hoped-for {:email test@foo.com, :code xyz}.

Should I be doing something differently? Is this even the right approach to getting the data?

ntalbs
  • 28,700
  • 8
  • 66
  • 83
pickwick
  • 3,134
  • 22
  • 30

3 Answers3

2

You need to leverage ring's wrap-params middleware, coupled with the wrap-keyword-params middleware which converts the params map to a key map.

(ns your.namespace
  (:require [ring.middleware.params :refer  [wrap-params]]
            [ring.middleware.keyword-params :refer  [wrap-keyword-params]]))

(def app
  (-> some-other-middleware
      wrap-keyword-params
      wrap-params))

Using this middleware with wrap-params converts params to use keys. After adding this middleware, you can access your params from the request map, like so (-> ctx :request :params). No need to convert them per request. This will handle all requests.

Seth
  • 10,198
  • 10
  • 45
  • 68
1

I just had to put "true" at the end of the call to the cheshire function, and the keys are returned as keywords:

(cheshire/parse-string params true)
pickwick
  • 3,134
  • 22
  • 30
  • I recommend leveraging ring's `wrap-keyword-params` and `wrap-params` middleware. This is much more dry, as you will have to parse params per each decision in liberator. Whereas with ring middleware, this is done for every request. Therefore already converted and accessible in each decision. – Seth Apr 18 '15 at 00:36
0

Depending on your requirements, you can simplify the handling of your post data using various ring middleware. This will allow you to process your json data in one place and eliminate the need to have duplicate data processing in each of your handlers/resource definitions. There are a few ways of doing this. You can have the json data added as keywordized parameters in the params map or a json-params map. Have a look at ring.middleware.format and ring.middleware.json.

Tim X
  • 4,158
  • 1
  • 20
  • 26
  • I did try that, but the middleware does not affect the data I'm slurping from the Liberator context results. – pickwick Apr 17 '15 at 03:31
  • The advantage of the middleware approach is you don't have to slurp the data from the response body. The ring middleware will take care of that for you. You can just get the params from the map which is already in the liberator context. – Tim X Apr 18 '15 at 05:02