Parsing JSON with wrap-json-body
I have been making good progress on the Clojure app, and then got to handling a PUT call, and realized that the current scheme I was using for parsing the :body of the compojure request really was not what I wanted. After all, the Content-type is set to application/json in the call, so there should be a way for the ring middleware to detect this, and parse the JSON body so that the :body of the request is a Clojure map, and not something that has to be parsed.
So I went digging...
As it turns out, there is a set of ring middleware for JSON parsing, and the middleware I needed was already written: wrap-json-body and to use it really is quite simple:
(:require [camel-snake-kebab.core :refer [->kebab-case-keyword]] [ring.middleware.json :refer [wrap-json-body]]) (def app "The actual ring handler that is run -- this is the routes above wrapped in various middlewares." (let [backend (session-backend {:unauthorized-handler unauthorized-handler})] (-> app-routes (wrap-access-rules {:rules rules :on-error unauthorized-handler}) (wrap-authorization backend) (wrap-authentication backend) wrap-user (wrap-json-body {:key-fn ->kebab-case-keyword}) wrap-json-with-padding wrap-cors (wrap-defaults site-defaults*) wrap-logging wrap-gzip)))
where the key middleware is:
(wrap-json-body {:key-fn ->kebab-case-keyword})
and it takes the ->kebab-case-keyword function to apply to each of the keys of the parsed JSON, and for me, that makes them keywords, and kebab-cased. This means I only have to have the right spelling in the client code, and I don't care a whit about the casing - very nice. 🙂
With this, an element of a defroutes can look like:
(POST "/login" [:as {session :session body :body}] (do-login session body))
and the body will be parsed JSON with keywords for keys, and the kebab case. You can't get much nicer than that. Clean, simple, code. Beautiful.