Fork me on GitHub
#reitit
<
2022-08-19
>
simongray10:08:07

Right now I just have this as my frontend routes:

(def routes
  [["{*path}" :delegate]])
This just delegates every path to my SPA and work very well for my needs. However, I also need to create some download links which should go directly to my backend. How can I make reitit frontend routing ignore certain routes while still matching everything else?

rolt10:08:47

you can disable conflicts and it should respect the route order

rolt10:08:26

i'd use a prefix though

simongray10:08:29

I think I need to bypass the routing though, right? The point is that I don’t want to match specific routes.

simongray10:08:55

When you click a download link in plain HTML, provided the HTTP resource identifies as downloadable file, the browser will initiate the download and keep the history state the same, i.e. the path doesn’t change to /path/to/file.ext . When Reitit matches a route, it inserts history and assumes that I manually initiate a download via CLJS, which I do no want to do — I want the regular browser behaviour, i.e. I want to bypass Reitit’s routing.

simongray10:08:16

The trouble is that I need to match every route in the frontend except for certain routes, not that I need to match certain routes in addition to every route.

simongray10:08:43

I guess I can use a prefix…

rolt12:08:43

i misunderstood the question, i don't know about that one

simongray12:08:16

I just went with a prefixed catch-all route and use a different prefix for the download routes. Works fine.

eval-on-point13:08:19

Is there a simple way to conditionally apply parameter coercion when using the ring coercion middleware? For example, say I have a route like this:

["" {:post {:parameters {:multipart ::spec/some-spec
                         :form ::spec/some-spec}
            :handler some-handler}}]
Can I make it such that either the multipart or form params are coerced, but not require both params to be there?

eval-on-point13:08:43

I suppose I can push it down into the handler but just wondering if there is something I am missing

eval-on-point14:08:57

yeah there might be a misunderstanding on my part, but I get a spec error receiving multipart requests with this basic setup. I assume the situation would be the same if I had :query there instead of :form. As in, I don't know if there is a way to accept a certain set of parameters either through :form or through :query

pppaul14:08:58

are the coercers implemented through interceptors?

eval-on-point14:08:28

We've been using the coercion middlewares, but I don't think there would be an issue moving to an interceptor

pppaul14:08:01

i'm just reading the docs right now, they are implemented via interceptors, unless i'm reading wrong

eval-on-point14:08:06

I think the specific coercers we are using are implemented as middleware here: [Pluggable Coercion — metosin/reitit 0.5.18](https://cljdoc.org/d/metosin/reitit/0.5.18/doc/ring/pluggable-coercion)

pppaul14:08:01

are you using ring?

eval-on-point14:08:38

yes, provided through reitit.ring. Sorry, probably should have been more specific at the start of my question

pppaul14:08:13

just looking at this, it seems like the multipart interceptor has no knowledge of other parameters

pppaul14:08:29

the way i have used multipart is mainly via form data (form submits, or faux form submits - ajax). and multipart is using the form coercer in that case

pppaul14:08:22

however, here multipart is independent of the other parameters, so in the form coercer it would have to check to see if multipart is present, and then not do it's thing.

pppaul14:08:03

https://github.com/metosin/reitit/blob/master/modules/reitit-core/src/reitit/coercion.cljc looking at this it seems like the coercers are independent, and the spec code https://github.com/metosin/reitit/blob/master/modules/reitit-spec/src/reitit/coercion/spec.cljc is where you would have to implement your idea of conditional coercion

(-request-coercer [this type spec]
      (let [spec (coercion/-compile-model this spec nil)
            {:keys [formats default]} (transformers type)]
        (fn [value format]
          (if-let [transformer (or (get formats format) default)]
            (let [coerced (st/coerce spec value transformer)]
              (if (s/valid? spec coerced)
                coerced
                (let [transformed (st/conform spec coerced transformer)]
                  (if (s/invalid? transformed)
                    (let [problems (st/explain-data spec coerced transformer)]
                      (coercion/map->CoercionError
                       {:spec spec
                        :problems problems}))
                    (s/unform spec transformed)))))
            value))))
probably in that code

eval-on-point14:08:55

cool I am looking at the same sort of thing as well. Thanks for the second set of eyes, I'll ping back if anything interesting comes of it.

pppaul15:08:47

i think the thing you want is very a-typical, make it so all your forms are multipart on that resource (this is my approach), and remove your :form declaration (is this possible?)

eval-on-point15:08:39

Yeah that is totally an option, but we would be more robust with a workaround. We are hooking a route up to receive messages from an email forwarding service that we don't control. The service only sends multipart if the email it is forwarding has an attachment. I think if this stymies me for much longer we will just require that anyone submitting data over email must not include attachments

eval-on-point15:08:39

It is definitely atypical and not great but these are the requirements I have 🤷

eval-on-point15:08:04

thanks again for the help!

pppaul15:08:27

i think you have to do something a bit hacky, and fork the spec code for a bit, maybe get your thing working, and then maybe add some feature for other people to do the same thing as you.

pppaul15:08:47

i never really liked the idea of splitting multipart away from the other submission stuff

pppaul15:08:01

it's so messy, every framework i look at it's just messy

pppaul15:08:26

also, maybe in the multipart middleware handler, you could strip off the form coercer or something...

pppaul15:08:54

multipart looks like it's a lot easier to deal with because it's less abstract

pppaul15:08:58

(-> request
                        (multipart-params/multipart-params-request options)
                        (coerced-request coercers)
                        (handler respond raise)))))
you have access to the whole request, just try and chop it up a bit 🙂

pppaul15:08:00

anyway, that was a fun dive into reitit source

eval-on-point17:08:28

Thanks again. I slept on it over the weekend and figured out I could get what I want by just adding a middleware that moves form params (if they exist) into the :multipart-params key. Then, I only set coercion for multipart parameters, and I get coercion for both. Kind of hacky but it meets the requirement