Fork me on GitHub
#reitit
<
2023-09-16
>
az00:09:12

Hi all, is it possible to have body params coerced? {:y "42"} -> {:y 42}? Seems like only validation is applied to body params am I missing something? I see path params will coerce from string to num but can't seem to get body to do the same. Thanks.

rickheere07:09:04

Hi, I have a route like this and it makes sure the string that comes in from a for is turned into an number

["/client/:client-id/cards/add"
 {:name ::card/add-card
  :post {:handler    (...)
         :coercion   reitit.coercion.malli/coercion
         :parameters {:form [:map
                             [:card-name string?]
                             [:card-index :int]]}}}]

rickheere07:09:25

You should be able to change :form to :body

az14:09:54

Hi @U4XT72NNT, yeah it's odd, I have tried spec, malli and schema and can't get it to coerce, only validate, will keep trying

rickheere14:09:06

And did you add the middleware? You probably only need the request one, I just added them all to be sure.

reitit.ring.coercion/coerce-exceptions-middleware
reitit.ring.coercion/coerce-request-middleware
reitit.ring.coercion/coerce-response-middleware

rickheere15:09:15

All together:

(ring/router
  [["/client/:client-id/cards/add"
    {:name ::card/add-card
     :post {:handler    (...)
            :coercion   reitit.coercion.malli/coercion
            :parameters {:form [:map
                                [:card-name string?]
                                [:card-index :int]]}}}]]
  {:data
   {:middleware
     reitit.ring.coercion/coerce-exceptions-middleware
     reitit.ring.coercion/coerce-request-middleware
     reitit.ring.coercion/coerce-response-middleware}})

az00:09:59

(comment
  (require '[reitit.ring.coercion :as rrc])
  (require '[reitit.coercion.schema])
  (require '[reitit.ring :as ring])
  (require '[schema.core :as s]) 

  (def app
    (ring/ring-handler
     (ring/router
      ["/api" 
       ["/plus/:z" {
                    :post {:coercion reitit.coercion.schema/coercion
                           :parameters {:query {:x s/Int}
                                        :body {:y s/Int}
                                        :path {:z s/Int}}
                           :handler (fn [{:keys [parameters]}]
                                      (let [total (+ (-> parameters :query :x)
                                                     (-> parameters :body :y)
                                                     (-> parameters :path :z))]
                                        {:status 200
                                         :body {:total total}}))}}]]
      {:data {:middleware [rrc/coerce-exceptions-middleware
                           rrc/coerce-request-middleware
                           rrc/coerce-response-middleware]}})))


  (app {:request-method :post
        :uri "/api/plus/3"
        :query-params {"x" "5"}
        :body-params {:y 6}})
  ;; => {:status 200, :body {:total 14}}
 


  (app {:request-method :post
        :uri "/api/plus/3"
        :query-params {"x" "1"}
        :body-params {:y "6"}})
  ;; => {:status 400,
  ;;     :body
  ;;     {:schema {:y "Int"},
  ;;      :errors {:y "(not (integer? \"6\"))"},
  ;;      :type :reitit.coercion/request-coercion,
  ;;      :coercion :schema,
  ;;      :value {:y "6"},
  ;;      :in [:request :body-params]}}


;;Keep from folding
  )

az00:09:28

This is what I get. Am I missing something here?

rickheere07:09:22

Man, I juist don't see the problem.

rickheere07:09:55

I tested out the code and I also get the validation error

rickheere07:09:21

And when I port the code to malli its the same thing

Marc Gannon12:09:23

I think if there is no format specified for the :body-params, then the default-coercion-matcher will be used (which just returns nil constantly) see here https://github.com/metosin/reitit/blob/master/modules/reitit-schema/src/reitit/coercion/schema.cljc#L40C48-L40C55. You can see if you change the coercion options so that the body default option is the string-coercion-matcher, it will work

(comment
  (require '[reitit.ring.coercion :as rrc])
  (require '[reitit.coercion.schema :as rcs])
  (require '[reitit.ring :as ring])
  (require '[schema.core :as s])

  (def app
    (ring/ring-handler
     (ring/router
      ["/api"
       ["/plus/:z" {:post {:coercion (rcs/create (assoc-in rcs/default-options [:matchers :body :default] rcs/string-coercion-matcher))
                           :parameters {:query {:x s/Int}
                                        :body {:y s/Int}
                                        :path {:z s/Int}}
                           :handler (fn [{:keys [parameters]}]
                                      (let [total (+ (-> parameters :query :x)
                                                     (-> parameters :body :y)
                                                     (-> parameters :path :z))]
                                        {:status 200
                                         :body {:total total}}))}}]]
      {:data {:middleware [rrc/coerce-exceptions-middleware
                           rrc/coerce-request-middleware]}})))


  (app {:request-method :post
        :uri "/api/plus/3"
        :query-params {"x" "5"}
        :content-type "application/json"
        :body-params {:y 6}}) => {:status 200, :body {:total 14}}


  (app {:request-method :post
        :uri "/api/plus/3"
        :query-params {"x" "1"}
        :body-params {:y "6"}}) => {:status 200, :body {:total 10}}
  
  )

❤️ 1
az19:09:45

@U053EMPAGEM Thank you!

❤️ 1