clojureliberator

Clojure Liberator: get request body twice


Let's assume, that I've got an endpoint POST /endpoint that takes JSON as input data:

POST /endpoint
{
  "resource": {
    "name": "great"
  }
}

Now, I want to check whether the incoming JSON is valid. I thought that :malformed? hook would be a great place for doing that.

So I came up with such a code:

(ANY "/endpoint" [] 
     (resource
      :allowed-methods [:post]
      :available-media-types ["application/json"]
      :malformed? (fn [ctx] (let [body (get-body-from-request ctx)
                                   is-malformed (not (is-body-valid body))]
                                is-malformed)
      :post! (fn [ctx] (let [body (get-body-from-request ctx)]
                          (create-an-object body)))
      :handle-created (fn [ctx] {:method :post :body (:body @request)})))

So my intention was:

The problem here is the get-body-from-request function:

(defn get-body-from-request [ctx]
  (json/read-str (slurp (get-in ctx [:request :body])) :key-fn keyword))

It works perfectly but the body is an InputStream, so I can read it only once.

Is there a way to use body of the request in more than one hook NOT using atoms?

The solutions coming to my mind:

  1. A middleware wrapper that adds a string version of body to the ctx param.
  2. Pass the result of malformed? to post!...
  3. Use atoms.

Thanks!


Solution

  • OK, it was pretty easy, there's of course a wrapper that does that...

    [ring-json-params "0.1.3"]

    Then you just wrap your app handler:

    (ns my.app
      (:require [ring.middleware.json :refer [wrap-json-params]]))
    
    (def app
      (-> handler
          (wrap-json-params)))
    

    and in your ctx object you've got a json-params object:

    :malformed? (fn [ctx] (let [params (get-in ctx [:request :json-params])]
                            false))