intended behaviour of wrap-json-response

27 views
Skip to first unread message

De Narm

unread,
Sep 1, 2019, 8:47:10 AM9/1/19
to Ring
Greetings,

I'm rather new to the whole topic and recently started playing around with ring. While writing some tests I've been surprised to find that "wrap-json-response" converts the body first into json and afterwards into a string, which I took some time to find it's intended behaviour but still can't wrap my head around why that's so. Could you please explain? Maybe I just searched the wrong thing overall but I couldn't find other instances of json to do this.

James Reeves

unread,
Sep 1, 2019, 8:55:29 AM9/1/19
to Ring
On Sun, 1 Sep 2019 at 13:47, De Narm <sebastia...@gmail.com> wrote:
I'm rather new to the whole topic and recently started playing around with ring. While writing some tests I've been surprised to find that "wrap-json-response" converts the body first into json and afterwards into a string, which I took some time to find it's intended behaviour but still can't wrap my head around why that's so.

The wrap-json-response function generates a JSON string all in one go. I don't understand what you mean by "converts the body first into json and afterwards into a string". JSON is a way of formatting a data structure as a string of text.

If you're asking if wrap-json-response supports streaming, as of version 0.5.0 it does.

--
James Reeves

Sebastian Benner

unread,
Sep 1, 2019, 9:47:51 AM9/1/19
to Ring
Am Sonntag, 1. September 2019 14:55:29 UTC+2 schrieb James Reeves:
The wrap-json-response function generates a JSON string all in one go. I don't understand what you mean by "converts the body first into json and afterwards into a string". JSON is a way of formatting a data structure as a string of text.

If you're asking if wrap-json-response supports streaming, as of version 0.5.0 it does.

--
James Reeves

I guess I should have included an example. Let's say I use something like this:
(ns test-rest-api.handler                                                      
 
(:require [ring.middleware.json :refer [wrap-json-body wrap-json-response]]  
           
[ring.util.response :refer [response]]                              
           
[org.httpkit.server :refer [run-server]]))                          
                                                                               
(defn handler [request]                                                        
   
(response {:foo "bar"}))                                                    
                                                                               
(defn -main [& args]                                                                    
   
(let [handler (-> handler                                                  
                   
(wrap-json-body)                                          
                   
(wrap-json-response))]                                      
       
(run-server handler {:port 8080})))

What I'd expect is something like:
{:status 200
 
:headers {"Content-Type" "application/json"},
 
:body {"foo": "bar"}}

Instead the acutal response is:
{:status 200
 
:headers {"Content-Type" "application/json"},
 
:body "{\"foo\": \"bar\"}"}

After looking it up I assumed it's intended, but why?

James Reeves

unread,
Sep 1, 2019, 1:20:18 PM9/1/19
to Ring
On Sun, 1 Sep 2019 at 14:47, Sebastian Benner <sebastia...@gmail.com> wrote:
Let's say I use something like this:
(ns test-rest-api.handler                                                      
 
(:require [ring.middleware.json :refer [wrap-json-body wrap-json-response]]  
           
[ring.util.response :refer [response]]                              
           
[org.httpkit.server :refer [run-server]]))                          
                                                                               
(defn handler [request]                                                        
   
(response {:foo "bar"}))                                                    
                                                                               
(defn -main [& args]                                                                    
   
(let [handler (-> handler                                                  
                   
(wrap-json-body)                                          
                   
(wrap-json-response))]                                      
       
(run-server handler {:port 8080})))

What I'd expect is something like:
{:status 200
 
:headers {"Content-Type" "application/json"},
 
:body {"foo": "bar"}}

Instead the acutal response is:
{:status 200
 
:headers {"Content-Type" "application/json"},
 
:body "{\"foo\": \"bar\"}"}

After looking it up I assumed it's intended, but why?

You seem to be a little confused as to what JSON is. JSON is a way of formatting data as a string of text. So data goes in and a string comes out, or vice versa.

Clojure doesn't understand JSON natively, so you can't just put JSON in the middle of a Clojure file and expect Clojure to be able to understand it.

user=> {"foo": "bar"}
Syntax error reading source at (REPL:1:8).
Invalid token: :

What wrap-json-response does is turn a Clojure map like:

{:foo "bar"}

Into a JSON formatted string like:

"{\"foo\": \"bar\"}"

--
James Reeves

Phill Wolf

unread,
Sep 1, 2019, 4:57:58 PM9/1/19
to Ring
The JSON you expected is safe-and-sound.  The response you observed is apparently a printed-out Clojure map.  Printing introduces some backslashes and quotes, the benefit of which is that the printout is a well-formed Clojure literal and can round-trip back in (with edn/read) as an equivalent map.  It is why you do not have to write code to serialize and deserialize your Clojure data structures.  Anyway, when the Ring adapter emits the response map as HTTP, it does not add those extra quotes and backslashes.

Sebastian Benner

unread,
Sep 1, 2019, 8:09:54 PM9/1/19
to Ring
Thanks to both of you, it finally seems obvious to me where I got it all
wrong and I'm grateful you took the time to explain it to me!


Reply all
Reply to author
Forward
0 new messages