Fork me on GitHub
#reitit
<
2024-01-05
>
Sturm00:01:42

Hi, what would be the best way to get Reitit to check that a URL path parameter is an integer during routing? eg. /customer/:id should match /customer/99 but /customer/abc shouldn't and should 404. I'm coming from Python/Django which has rexexp-type patterns and coercion built into the routing, eg. path('/customer/<int:id>/', handler). I've set up spec coercion with Reitit, but the route will match any :id value and return a 400 with the spec JSON, rather than failing to match.

hifumi12309:01:12

you should be able to capture the coercion error and handle it yourself to return a 404… very manual process but doable

Sturm12:01:39

Right, I think I'm starting to get my head around reitit a little. So by design, both (r/match-by-path router "/customer/99") and (r/match-by-path router "/customer/abc") will return a match for "/customer/:id" - there's no way at the router level to match if the parameter looks like an integer? That concern is left to the middleware or handler?

Sturm12:01:20

This is my first naive pass at a middleware to convert these spec coercion errors into a 404:

(def wrap-spec-coercion-error
  {:name ::wrap-spec-coercion-error
   :wrap (fn [handler]
           (fn [request]
             (let [response (handler request)]
               (if (get-in response [:body :problems])
                 {:status 404 :body "Path invalid" :headers {"Content-Type" "text/html"}}
                 response))))})

Sturm01:01:46

(This is a server-side HTML app I'm working on, rather than an API, so I'm hoping to return a friendly 404 page rather than the spec error JSON)

István Karaszi18:01:41

I need the raw submitted body of the POST request, but I don’t know how can I do that? I did something like this:

{:post {:parameters {:body any?}
        :response {200 {:body any?}}
        :handler payment/webhook}}
But it parses the body and I cannot get the raw body in any way. I’ve tried (ring.util.request/body-string request) but that returns an empty String, I guess because the body was already parsed

István Karaszi18:01:31

if I remove the :parameters then it does not parse the params but the body is still not available

Patrix03:01:34

I've done this by creating a new wrap middleware, and then wrapping my route(s) with that middleware. My use-case was validating Stripe webhook calls, where I need the raw body to compute its signature and compare it against the signature in the request.

(defn wrap-raw-body [handler]
  (fn [request]
    (let [buffered-body (slurp (:body request))
          body-stream (io/input-stream (.getBytes buffered-body))
          parsed-body (json/parse-string buffered-body keyword)]
      (handler (assoc request :body body-stream :raw-body buffered-body
                      :body-params parsed-body)))))

👍 1
Patrix03:01:10

(I've been told slurp might not be a good idea in prod but for now, it's what I have)

István Karaszi08:01:39

This is exactly the reason I need it. Thank you! I am sure it can be done with a middleware or an interceptor, but I thought it is possible somehow without that.

1
István Karaszi09:01:48

Then I guess I’ll need to create an interceptor that can be enabled on certain endpoints to return the raw body.