Fork me on GitHub
#reitit
<
2022-12-07
>
Ben Sless13:12:44

Iirc, reitit does not support per-method middleware How much of an effort would you estimate it would be? Is there interest in such a feature?

jeroenvandijk13:12:21

Do you mean this?

(require '[reitit.ring :as ring])

(let [handler (ring/ring-handler (ring/router ["/ping" {:get {:middleware [(fn [handler]
                                                                             (fn [request]
                                                                               {:status 400}
                                                                               ))]
                                                              :handler (fn [_] {:status 200})}
                                                        :post {:middleware [(fn [handler]
                                                                             (fn [request]
                                                                               {:status 500}
                                                                               ))]
                                                              :handler (fn [_] {:status 200})}}]))]
  (handler {:request-method :get
            :uri "/ping"})

  (handler {:request-method :post
              :uri "/ping"}))

Ben Sless13:12:09

Wait, that works?

jeroenvandijk13:12:16

Here it does 🙂

Ben Sless14:12:33

How will that work with a global "compile" middleware

jeroenvandijk14:12:35

I don’t know about the order (by heart), but it will be mixed

Ben Sless14:12:50

Not in terms of order, I mean, I want to provide a compile middleware that will operate per-method. Is that possible?

jeroenvandijk14:12:29

I think i’m using it that way, but now I have trouble making a proper example

jeroenvandijk14:12:55

ok I was missing your compile requirement. Need to find a good compile example first. Do you have one?

jeroenvandijk14:12:04

@UK0810AQ2 I think it works, but Im probably missing something about the compiling

(let [wrap-label
      {:compile (fn [{:keys [my-data]} _]
                  (fn [handler]
                    (fn [request]                       
                      (update (handler request)
                              :middleware (fnil conj []) my-data))))}
      handler (fn [_] {:status 200})
      api (ring/ring-handler (ring/router ["/ping" {:my-data :intermediate
                                                    :middleware [wrap-label]
                                                    :get {:my-data :get
                                                          :middleware [wrap-label]
                                                          :handler handler}
                                                    :post {:my-data :post
                                                           :middleware [wrap-label]
                                                           :handler handler}}]

                                          {:data {:my-data :global
                                                  :middleware [wrap-label]}}))]
  (api {:request-method :get
        :uri "/ping"})

  (api {:request-method :post
        :uri "/ping"})

  )

jeroenvandijk14:12:00

I think the middleware needs to be unique

jeroenvandijk14:12:50

ok maybe you are right and the compiled version doesn’t work (as expected) when combined :man-shrugging:

Ben Sless14:12:25

Bummer, would have been nice

jeroenvandijk14:12:24

This one works

(let [wrap-label0
      {:compile (fn [{my-data :my-data0} _]
                  (fn [handler]
                    (fn [request]                       
                      (update (handler request)
                              :middleware (fnil conj []) my-data))))}
      wrap-label1
      {:compile (fn [{my-data :my-data1} _]
                  (fn [handler]
                    (fn [request]                       
                      (update (handler request)
                              :middleware (fnil conj []) my-data))))}
      wrap-label2
      {:compile (fn [{my-data :my-data2} _]
                  (fn [handler]
                    (fn [request]                       
                      (update (handler request)
                              :middleware (fnil conj []) my-data))))}
      handler (fn [_] {:status 200})
      api (ring/ring-handler (ring/router ["/ping" {:my-data1 :intermediate
                                                    :middleware [wrap-label1]
                                                    :get {:my-data2 :get
                                                          :middleware [wrap-label2]
                                                          :handler handler}
                                                    :post {:my-data2 :post
                                                           :middleware [wrap-label2]
                                                           :handler handler}}]

                                          {:data {:my-data0 :global
                                                  :middleware [wrap-label0]}}))]
  (api {:request-method :get
        :uri "/ping"})

  (api {:request-method :post
        :uri "/ping"})
  )

jeroenvandijk14:12:33

It is executed at compile time so you also need to reason about it a bit differently 😅

jeroenvandijk14:12:35

output above is {:status 200 :middleware [:post :intermediate :global]}

jeroenvandijk15:12:20

All route data is merged before it is fed to the middleware compiler. So reusing middleware at multiple levels and taking this data as input doesn’t really make sense

Ben Sless15:12:23

My goal here is per method instrumentation

jeroenvandijk15:12:03

Maybe you can give a small example?

Ben Sless15:12:28

I want to perform some side effect per route endpoint per method,like increment a counter Not at the computer at the moment so hand waving

jeroenvandijk15:12:08

Ah i see. I think it’s possible. I just wonder now if you need the compiled middleware. I think the benefit of compiled middleware is that you can skip things, but that’s not what you want then

Ben Sless15:12:17

I want to compile the middleware because the counters exist in a shared registry and I want to wrap with them instead of look them up

jeroenvandijk15:12:28

Yeah I’m sure everything is possible. Just need to try some things

jeroenvandijk15:12:41

If you can give an example I can help you further

jeroenvandijk15:12:07

You can manually feed all the data you need per endpoint (e.g. the fn that needs to update the registry). Maybe what is missing is a way to know where you are just from the route data. That would allow you to it automatically I guess

Ben Sless16:12:11

That's exactly what I want to do. I already have the route data I care about (verb, endpoint name), why feed it manually?

jeroenvandijk16:12:53

Then I think you are set. I’m just missing what you want to do globally then

jeroenvandijk16:12:43

As in global middleware. Your registry is global, but it’s not like in my examples where you reuse the same middleware on multiple levels. I think