Fork me on GitHub
#reitit
<
2019-04-29
>
conan11:04:42

I'm trying to work out how to get JSON out of reitit; I always get back application/octet-stream, whatever accept header I use. Here's my app:

(s/def ::thing (s/keys :req [::id string?]))

(defn get-thing
  [{{thing-id :thing-id} :path-params :as _request}]
  (response/ok {::id thing-id}))

(defn app
  []
  (rring/ring-handler
    (rring/router
      [""
       ["/things" {:middleware [[defaults/wrap-defaults defaults/api-defaults]]}
        ["/:thing-id"
         ["" {:name ::get-thing
              :parameters {:path {:thing-id string?}}
              :get {:responses {200 {:body ::thing}}
                    :handler get-thing}}]]]]
      {:data {:coercion rcs/coercion
              :muuntaja muuntaja/instance
              :middleware [rrmm/format-middleware
                           rcoercion/coerce-exceptions-middleware
                           rcoercion/coerce-request-middleware
                           rcoercion/coerce-response-middleware]}
       :exception pretty/exception})
    (-> (rring/create-default-handler)
        (defaults/wrap-defaults defaults/api-defaults))))
It's my understanding that the reitit.ring.muuntaja.middleware/format-middleware is the bit that should be responsible for doing content negotiation, and sure enough if I log my request I see this:
:muuntaja/request
 {:format "application/json",
  :charset "utf-8",
  :raw-format "application/json"}
Logging the response after the middleware shows me that the body is an EDN map. What am I missing to convert it to JSON?

valerauko11:04:34

I wrap the whole app with muuntaja.middleware/wrap-format and wrap-defaults like

(def wrapped-handler
  (-> (ring/ring-handler
        router
        (ring/routes
          (create-swagger-ui-handler {:path "/swagger" :jsonEditor true})
          (ring/redirect-trailing-slash-handler)
          (ring/create-default-handler
            {:not-found (constantly {:status 404 :body {:error "Not found"}})})))
      wrap-format
      (wrap-defaults api-defaults)))

conan11:04:16

so i think reitit.ring.muuntaja.middleware/format-middleware does the wrap-format for me, and i have the api-defaults in there, unless i've put them in the wrong place

valerauko11:04:16

my router is very similar setup to yours

ikitommi11:04:52

@conan ring-defaults has wrap-content-type, which sets the content-type if not present. Sadly, Muuntaja only encodes if there is no content-type, see https://github.com/metosin/muuntaja/issues/96

ikitommi11:04:30

but, as @vale said, if you reorder the mw, it should work.

conan11:04:39

ah ok, let my try excluding the wrap-content-type

valerauko11:04:48

just put the default-middleware on the outside

ikitommi11:04:57

I think the the ring-defaults behavior should have a pre-integrated version in reitit, reading the config (at least partially) from route-data.

conan11:04:55

yes, that's it! thank you, it would have taken me a long time to make that connection

ikitommi11:04:56

the wrap-session has issues, could be solved by doing a reitit.ring.middleware.session/session-middleware

ikitommi11:04:33

we need so much better tools for analyzing the middleware/interceptor chains.

conan11:04:55

this sort of interaction is always going to be very difficult to surface

conan11:04:28

so yes, a reitit-driven way of adding the defaults would be a good way of addressing it

ikitommi11:04:53

I guess the middleware chain differ doesn’t show responses currently?

ikitommi12:04:35

yes, it prints only requests. could print out response diffs too.

ikitommi12:04:44

well, it will. That helps a bit.

ikitommi12:04:59

The differ uses puget & 4bit ansi, kinda horrible. Could wrap that into the reitit-fipp pretty printer.

roklenarcic14:04:14

I am trying to have routes with subroutes with ring router:

["/withdrawal/{id}" {:get get-withdrawal}
          ["/confirm" {:post confirm-withdrawal}]]

roklenarcic14:04:22

but this seems to not work for the get endpoint

roklenarcic14:04:32

It doesn't get matched

valerauko14:04:09

maybe you try to access it at /withdrawal/{id}/? (note trailing slash)

roklenarcic14:04:57

I tried looking that compiled router

roklenarcic14:04:14

and I see that the route doesn't appear in wildcard routes

valerauko14:04:19

but /withdrawal/{id}/confirm works?

ikitommi14:04:47

@roklenarcic this should work:

["/withdrawal/{id}" 
   ["" {:get get-withdrawal}]
   ["/confirm" {:post confirm-withdrawal}]]