0
votes

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:

  • get the body from request, check if it's valid (is-body-valid)
  • in post! hook, use the body again and do real work (create-an-object)

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!

1

1 Answers

2
votes

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))