Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing POST json in clojure

EDIT - The source code is on github if you're interested. Thanks


I am a bit confused as to how to access json data that has been posted to a url in clojure; I just can't seem to get it to work.

This is my route:

(cc/POST "/add" 
request
(str ": " request))

I am not entirely sure what I have to put in place of request - I just saw some blog online and tried to follow it, but couldn't get it to work.

Here's how I'm trying to post: (from fiddler)

fiddler posting data

note: request header port is different in the image; it's a mistake, I was trying to mess with that data to see what it says so ignore that part in the above image please

In curl, I'm just doing this:

curl -X POST -H "Content-Type: application/json" -d '{"foo":"bar","baz":5}' 
     http://localhost:3005/add

It looks like clojure is not receiving the json data that I posted at all.

Here's what the request var contains:

: {:scheme :http, :query-params {}, :form-params {}, :request-method :post, 
   :query-string nil, :route-params {}, :content-type "\"application/json\"", 
   :uri "/event", :server-name "localhost", :params {}, 
   :headers {"user-agent" "Fiddler", "content-type" "\"application/json\"", 
          "content-length" "23", "host" "localhost:3005"}, 
   :content-length 23, :server-port 3005, :character-encoding nil, :body #}

As you can see, all params are empty...

I am using compojure and cheshire - and I can convert data into json and return them just fine for GET routes.. I need to figure out how to pass json and convert it into clojure data..

thanks

like image 798
LocustHorde Avatar asked Feb 05 '13 13:02

LocustHorde


2 Answers

That's because :params is filled by a ring middleware which deals with "form-encoded" body.

You can use ring-json to wrap your application into this other middleware. It will parse the JSON body and fill :params accordingly. (https://github.com/ring-clojure/ring-json)

like image 88
tomferon Avatar answered Sep 27 '22 22:09

tomferon


Here is an example that does what you want. The code was taken from this project. In the README you will see some of the access patterns that this supports. The code is a little messy, but it should illustrate how this can be done.

(ns poky.protocol.http.jdbc.text
  (:require [poky.kv.core :as kv]
    (compojure [core :refer :all]
               [route :as route]
               [handler :as handler])
    [ring.util.response :refer [response not-found]]
    (ring.middleware [format-response :as format-response ]
                     [format-params :as format-params])
    [cheshire.core :as json]
    [ring.middleware.stacktrace :as trace]))

;
; curl -d"some data" -H'Content-Type: application/text' -v -X PUT http://localhost:8080/xxx
; curl -d'{"bla":"bar"}' -H'Content-Type: application/json' -v -X PUT http://localhost:8080/bla

(def valid-key-regex #"[\d\w-_.,]+")

; FIXME: this should be split- one fn for get, one for mget
(defn- wrap-get
  [kvstore ks params headers body]
  (response 
    (let [eks (clojure.string/split ks #",")
          nks (count eks)
          multi (> nks 1)
          ret (if multi (kv/mget* kvstore eks) (kv/get* kvstore ks))]
    (condp = (get headers "accept")
      "application/json" ret
      "text/plain" (if multi (throw (Exception. "Multi get unsupported with Accept: text/plain")) (get ret ks))
      ret))))



(defn- wrap-put
  [kvstore ks params headers body]
  (if (and 
        (= (get headers "content-type") "application/json")
        (get params (keyword ks) nil))
    (kv/set* kvstore ks (get params (keyword ks)))
    (kv/set* kvstore ks body))
  (response ""))

(defn api
  [kvstore]
  (let [api-routes
        (routes
          (GET ["/:ks" :ks valid-key-regex] {{:keys [ks] :as params} :params body :body headers :headers}
               (wrap-get kvstore ks params headers body))
          (PUT ["/:ks" :ks valid-key-regex] {{:keys [ks] :as params} :params 
                                             body :body body-params :body-params headers :headers} 
               (let [body (slurp body)
                     body (if (empty? body) body-params body)]
                 (wrap-put kvstore ks params headers body))))]

    (-> (handler/api api-routes)
        (format-params/wrap-format-params
          :predicate format-params/json-request?
          :decoder #(json/parse-string % true)
          :charset format-params/get-or-guess-charset)
        (format-response/wrap-format-response
          :predicate format-response/serializable?
          :encoders [(format-response/make-encoder json/encode "application/json")
                     (format-response/make-encoder identity "text/plain")]
          :charset "utf-8")
        trace/wrap-stacktrace)))

Hope this helps.

like image 41
Damon Snyder Avatar answered Sep 27 '22 22:09

Damon Snyder