Fork me on GitHub
#reitit
<
2022-01-13
>
Marco Schneider15:01:01

Hey everyone. I’m still getting the hang of reitit (which I really like so far). I have just realized that my responses don’t seem to be coerced (requests are, though). I’m using reitit.coercion.schema and muuntaja. Below is part of an example (please ask if you need more details), which shows that responses are not coerced. I would be super happy if someone had the time to try and explain what I’m doing wrong here. From the router:

...
  (ring/ring-handler
   (ring/router
    [["/" (handle-index config)]
     ...
     ["/api"
      {:swagger {:tags ["api"]}}
      (make-endpoints run-m)]]
    {:data {:muuntaja   m/instance
            :coercion   schema/coercion
            :middleware [swagger/swagger-feature
                         ;; query-params & form-params
                         parameters/parameters-middleware
                         ;; content-negotionation, request and
                         ;; response en-/decoding.
                         muuntaja/format-middleware
                         ;; exception handling
                         wrap-exception
                         ;; coercing response bodys
                         coercion/coerce-response-middleware
                         ;; coercing request parameters
                         coercion/coerce-request-middleware
                         ;; multipart
                         multipart/multipart-middleware
                         wrap-request-logger
                         wrap-ignore-cors]}})
   (ring/routes
    (swagger-ui/create-swagger-ui-handler
     {:path   "/api"
      :config {:validatorUrl     nil
               :operationsSorter "alpha"}})
    (ring/create-default-handler)))
  ...
  
And the code that triggers the exception:
(let [port           (find-free-port)
      config         (active-config/make-configuration config/schema [] {:webserver {:port port}})
      make-endpoints (fn [_]
                       ["/test-endpoint"
                        {:get {:parameters {:query {:id     s/Uuid
                                                    :number s/Int
                                                    :string s/Str}}
                               :responses  {200 {:body {:id     s/Uuid
                                                        :number s/Int
                                                        :string s/Str}}}
                               :handler
                               (fn [{{{:keys [id number string]} :query} :parameters}]
                                 {:status  200
                                  :headers {"Content-Type" "application/json"}
                                  :body    {:id     id
                                            :number number
                                            :string string}})}}])
      {:keys [id number string]}
      {:id (effect/get-uuid!), :number 42, :string "string"}]
      (try
        (webserver/init-server! config ... make-endpoints)
        (let [{:keys [headers status body] :as resp}
              (http-client/get (str ":" port "/api/test-endpoint")
                               {:accept       :json
                                :query-params {:id     (util/show-uuid id)
                                               :number number
                                               :string string}})]
          (t/is (= (json/generate-string {:id     (util/show-uuid id)
                                          :number number
                                          :string string})
                   body)))
	...
        (finally (webserver/stop-server!))))
  
Which results in the following exception (which makes me belive the coercion of the response map never takes place):
...
  :body
  "{\n\"cause0\":\"java.lang.IllegalArgumentException: No implementation of method: :write-body-to-stream of protocol: #&apos;ring.core.protocols/StreamableResponseBody found for class: clojure.lang.PersistentArrayMap\",\n\"message\":\"java.lang.IllegalArgumentException: No implementation of method: :write-body-to-stream of protocol: #&apos;ring.core.protocols/StreamableResponseBody found for class: clojure.lang.PersistentArrayMap\",\n\"url\":\"/api/test-endpoint\",\n\"status\":\"500\"\n}",
  ...