reitit 2024-10-15

Something small for you I hope, would appreciate an explanation as to why when I make a test (app {:request-method :get ...}) call to an endpoint that returns a map, I always get a ByteArrayInputStream (which is JSON when slurped), instead of just a straight edn map? my route definition for this is

{:get {:summary "hello as a map"
       :handler (fn [_] {:status 200 :body {:hello "world"}})}}
and my route-data is
{:validation ring.spec/validate
    :exception ring.pretty/exception
    :data {:coercion coercion.malli/coercion
           :muuntaja m/instance
           :middleware [openapi/openapi-feature
                        ;; query-params & form-params
                        ring.parameters/parameters-middleware
                        ;; content-negotiation
                        ring.muuntaja/format-negotiate-middleware
                        ;; encoding response body
                        ring.muuntaja/format-response-middleware
                        ;; exception handling
                        ring.exception/exception-middleware
                        ;; decoding request body
                        ring.muuntaja/format-request-middleware
                        ;; coercing response body
                        ring.coercion/coerce-response-middleware
                        ;; coercing request parameters
                        ring.coercion/coerce-request-middleware
                        ;; managing multipart requests
                        ring.multipart/multipart-middleware]}}
I'm still attempting to understand ring/reitit a little better, as im still doing a lot of copying off of examples atm, so I might have some fundamental misunderstandings

I would expect a call to this endpoint to return

{:status 200
 :body {:hello "world"}}
similar to the example on reitit's github, but instead the body returns as a bytestream which is "{\"hello\":\"world\"}" when slurped

ah so as i've been rubber-ducking here it looks like its due to the format-response-middleware, which i find odd as its docstring doesn't really imply that behavior? to me it reads as if it should be affected by passing an {"Accept" "<content-type>"} header but that appears to have no bearing?

Not sure if this is documented properly, and this goes through several layers, but Muuntaja (the lib doing the response body encoding) defaults to json if the request doesn't have Accept header: https://github.com/metosin/muuntaja/blob/0.6.10/modules/muuntaja/src/muuntaja/core.clj#L116

If you have the full ring handler, there isn't really case where you would want to return the clojure data structures as the http servers themselves can't use them for the response body. For unit testing... Hmm. If you don't care for testing the response encoding, you could build the handler fn you call in unit tests without this middleware.

https://github.com/metosin/reitit/blob/master/modules/reitit-middleware/src/reitit/ring/middleware/muuntaja.clj#L43-L47 This docstring could mention that in addition to the Accept headers, also the Muuntaja config (`:default-format`) also affects the result.

hmmm, when im doing a test call like

(app {:request-method :get
      :uri "..."
      :headers {"Accept" "application/edn"}})
is this an incorrect way to use an accept header?

i actually keep having difficulty finding the reference-documentation for valid/useful keys in this request map

yes, that should be correct

that should return a bytestream which slurps to edn string

ah i see, i very much appreciate the help, this is still an area im less confident in

> :headers > A Clojure map of lowercased header name strings to corresponding header value strings.

lowercased, oh this has caused me much confusion

thank you for the link gratitude

Ah right. On the request side Ring spec says the should be always lower case. AFAIK on the response side it "doesn't matter" (except when you have multiple middleware that are checking for the same response header...)

Spec says to also use lower case headers names for response though

ahh that makes sense, i guess in my mind i assumed it would be sent to whatever the requisite case is, as in other webservers i've interacted with casing has been more loosey-goosey (and in stuff like Mozilla docs I always see headers written in Capital-Case)

(i personally prefer lowercase-as-default, have just been conditioned otherwise)

haha in fact right above it