Fork me on GitHub
#pedestal
<
2023-09-30
>
Patrix06:09:40

I have an interceptor where, in the (post) request, I'm trying to access json params, so ok we have the :json-params map. But I also want to access the raw POST data as a string. I see there's also a :body key in the :request, however it's some Java object. (org.eclipse.jetty.server.HttpInputOverHTTP). Not sure how to proceed from here as my familiarity with the java/jetty land is quite minimal... So essentially, what would be the recommended way of getting that POST body as a string?

Patrix05:10:47

So I figured I can (slurp) that object, however that yields an empty string. Probably because, as I just learned, Output streams can be read only once... And I'm guessing the stream is read throughout all the other interceptors that get the json params for example.

souenzzo21:10:47

you may want to not use body-param interceptor body parameter interceptor will not provide access to the raw body you probably need something like this:

(def my-json-interceptor
  {:name  ::my-json-interceptor
   :enter (fn [{:keys [request]
                :as   ctx}]
            (if
              ;; maybe check if method=post and content-type=json
              (-> request :request-method 
                #{:post :put '...})
              (let [raw-body-str (-> request :body slurp)
                    json-params (json/parse-string raw-body-str true)]
                (-> ctx
                  (assoc-in [:request :json-params] json-params)
                  (assoc-in [:request :raw-body-str] raw-body-str)))
              ctx))})

Patrix23:10:55

Ahhh ok so for this one route, I should probably not use the default interceptors? So I probably shouldn't put it in my service map then, as that would apply to every route..

Patrix23:10:21

thanks @U2J4FRT2T, @UDXEK491P, let me try these...

Patrix09:10:18

Ok, this seems to be working! Now I'm able to get that raw body string and validate it with the stripe webhook secret key, whew. And still can get the json params to proceed with actually handling the events was able to remove the body-params interceptor from the service map, and move that into the other function I had which added a bunch of interceptors, but only for the routes that need that, instead of all routes.

Patrix09:10:31

thanks for the tips/direction, that was straightforward enough 🙂

Patrix11:10:30

ok I didn't need to refactor all that but it helped me simplify a few things sooo it's ok, I think

souenzzo10:10:23

Btw, do not use slurp in prod. Check HttpInputBody docs to see how to read properly

✅ 1
Patrix00:10:43

@U2J4FRT2T could you help with some pointers where I can read more about both of those? (why not use slurp in prod.. and how to read from an HttpInputBody ?)

souenzzo09:10:59

slurp is a easy, but not so simple tool I see slurp as a "dev helper" function. For prod, I'd write a function like this

(defn req-body->str 
  [{:keys [^InputStream body]}]
  (String. (.readAllBytes body)))

✅ 1
Patrix00:10:35

ahh ok, so because the body object is a subclass of an InputStream (can't remember how I figured that out a week ago), then I can use the methods of InputStream to access it, rather than search through Jetty docs to figure that out. Got it! Thanks @U2J4FRT2T