Coercion isn't applied to :body-params
agigao opened this issue · comments
Recently, I've come across a rather confusing coercion behaviour - under :body-params
coercion just throws validation error and under :form-params
it does the coercion:
(def app
(ring/ring-handler
(ring/router
[
["/" {:name :root
:get (fn [_] {:status 200 :body "pong"})
:post {:coercion rcm/coercion
:parameters {:body [:map [:id uuid?]]}
:handler (fn [{:keys [parameters]}]
{:status 200
:body (-> parameters :form)})}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]}})))
(app
{:request-method :post
:uri "/"
:body-params {:id "644c1b76-4c3c-4067-8aeb-86236bcb5538"}})
;; Result
{:status 400,
:body {:schema "[:map {:closed true} [:id uuid?]]",
:errors ({:path [:id],
:in [:id],
:schema "uuid?",
:value "644c1b76-4c3c-4067-8aeb-86236bcb5538",
:message "should be a uuid"}),
:value {:id "644c1b76-4c3c-4067-8aeb-86236bcb5538"},
:type :reitit.coercion/request-coercion,
:coercion :malli,
:in [:request :body-params],
:humanized {:id ["should be a uuid"]}}}
If I use :form-params
instead, it works:
(def app
(ring/ring-handler
(ring/router
[
["/" {:name :root
:get (fn [_] {:status 200 :body "pong"})
:post {:coercion rcm/coercion
:parameters {:form [:map [:id uuid?]]}
:handler (fn [{:keys [parameters]}]
{:status 200
:body (-> parameters :form)})}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]}})))
(app
{:request-method :post
:uri "/"
:form-params {:id "644c1b76-4c3c-4067-8aeb-86236bcb5538"}})
;; Result
{:status 200, :body {:id #uuid"644c1b76-4c3c-4067-8aeb-86236bcb5538"}}
We found out that it's related to the default-options
's default
transformer that provided under body
.
(reitit.coercion.malli/create
(-> reitit.coercion.malli/default-options
(assoc-in [:transformers :body :default]
reitit.coercion.malli/string-transformer-provider)))
This s-expression can help coerce
things in :body
.
If it's not the case, please provide information. I might be proven wrong.
It depends on what is your input content-type value.
UUID decoder is included in JSON transformer (and thus is also inherited by string transformer): https://github.com/metosin/malli/blob/master/src/malli/transform.cljc#L223
But if your request content-type is something else, e.g., Transit, no transformer is enabled, as the format might be able to represent the UUID type natively (as Transit can).
If you want to test JSON coercion, set content-type on your request, and encode your request body. (Note that JSON decoding also requires Muuntaja middleware.)
If you want to write test cases and you are going to use Transit with the API, I'd just use UUID types in the test code.
Nice answer @Deraen. Please reopen if you still have a problem!