Search code examples
clojureringre-frame

Parse JSON body from HTTP request (with ring and re-frame-http-fx)


I have a re-frame-based UI and try to communicate with my server using re-frame-http-fx. Sending and responding seems to work. However, I can't figure out how to parse the JSON body into a Clojure map on the server.

Here is my handler.clj as minimal as I could get it:

(ns my.handler
  (:require [compojure.core :refer [GET POST defroutes]]
            [compojure.route :refer [resources]]
            [ring.util.response :refer [resource-response]]
            [ring.middleware.json :refer [wrap-json-response wrap-json-body]]))

(defn json-post [request]
  (let [body (:body request)]
    (prn body)
    body))

(defroutes routes
  (GET       "/"     []      (resource-response "index.html" {:root "public"}))
  (POST      "/post" request json-post)
  (resources "/"))

(def handler (wrap-json-response (wrap-json-body routes {:keywords? true})))

As far as I understand, the wrap-json-body middleware should replace the request body by a parsed version (a map?).

However, the output I get from (prn body) in my json-post handler is something like this: #object[org.httpkit.BytesInputStream 0xda8b162 "BytesInputStream[len=41]"]

If I try something like (prn (:title body)) I get nil (although the original map-turned-json-request contains :title, as well as both the request and response body).

The request and response contain the correct json. The request Content-Type is correctly set to application/json (sent by re-frame-http-fx). The length of the buffer (41) is also the correct body length as per request.

I am running out of things to try. Any ideas?


Solution

  • While investigating the issue further, I found my error that lead to the effect. It concerns the dev-handler from the re-frame template that I conventiently omitted from my minimal example in the question.

    I did not realize it was a problem, because the application seems to start fine even if you delete the entire definition of dev-handler from handler.clj, I assume because the server is initialized with handler in server.clj anyway (and the client does not fail fatally).

    However, in project.clj of the re-frame template, the following is configured for figwheel:

      :figwheel {:css-dirs ["resources/public/css"]
                 :ring-handler my.handler/dev-handler}
    

    This leads to middleware configured for handler not being applied to my requests, thus not unwrapping the json body. Changing either the definition of dev-handler (the the same as handler in the question) or the configuration of figwheel in project.clj (to point to handler instead of dev-handler) solves the problem.

    If anybody knows the reasoning of different handlers in project.clj and server.clj, feel free to let me know.