-
Notifications
You must be signed in to change notification settings - Fork 262
Description
Given:
(require
'[reitit.http :as http]
'[reitit.http.coercion :as coercion]
'[reitit.http.interceptors.parameters :as parameters])
(def api-chain [parameters/parameters-interceptor
coercion/coerce-response-interceptor
coercion/coerce-request-interceptor])
(def my-interceptor
{:enter (fn [ctx] ctx)})
(http/router ["/" {:interceptors [api-chain my-interceptor]}])Result:
Wrong number of args (2) passed to:
reitit.http.interceptors.parameters/parameters-interceptor
{:reitit.exception/cause #error {
:cause "Wrong number of args (2) passed to: reitit.http.interceptors.parameters/parameters-interceptor"
:via
[{:type clojure.lang.ArityException
:message "Wrong number of args (2) passed to: reitit.http.interceptors.parameters/parameters-interceptor"
:at [clojure.lang.AFn throwArity "AFn.java" 429]}]
:trace
[[clojure.lang.AFn throwArity "AFn.java" 429]
[clojure.lang.AFn invoke "AFn.java" 36]
[clojure.lang.AFn applyToHelper "AFn.java" 156]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.core$apply invokeStatic "core.clj" 667]
[clojure.core$apply invoke "core.clj" 662]
[reitit.interceptor$eval46785$fn__46787 invoke "NO_SOURCE_FILE" 58]
[reitit.interceptor$eval33607$fn__33608$G__33596__33617 invoke "interceptor.cljc" 7]
[reitit.interceptor$chain$fn__34004 invoke "interceptor.cljc" 113]
...Expected result:
I expect the resulting interceptors chain for "/" to be:
[parameters/parameters-interceptor
coercion/coerce-response-interceptor
coercion/coerce-request-interceptor
my-interceptor]Extra
@ikitommi indicated that this should work:

(slack link)
However, looking at the implementation, it cannot work, because the :interceptors vector in route data is processed one element at a time:
reitit/modules/reitit-core/src/reitit/interceptor.cljc
Lines 103 to 116 in 0f94148
| (defn chain | |
| "Creates a Interceptor chain out of sequence of IntoInterceptor | |
| Optionally takes route data and (Router) opts." | |
| ([interceptors] | |
| (chain interceptors nil nil)) | |
| ([interceptors data] | |
| (chain interceptors data nil)) | |
| ([interceptors data {::keys [transform] :or {transform identity} :as opts}] | |
| (let [transform (if (vector? transform) (apply comp (reverse transform)) transform)] | |
| (->> interceptors | |
| (keep #(into-interceptor % data opts)) | |
| (transform) | |
| (keep #(into-interceptor % data opts)) | |
| (into []))))) |
So it is not possible for the into-interceptor implementation for clojure.lang.APersistentVector to flatten the vector, instead it assumes you're doing a delayed function call: [some-interceptor-ctor :a-param :another-param] -> (some-interceptor-ctor :a-param :another-param) (I'm curious what the usecase for that is btw?)
Workarounds
We would like this feature, as defining a stack, or chain, of interceptors that can be re-used and composed is very useful.
Unfortunately there aren't any easy workarounds.
-
:reitit.interceptor/transformi.e.,(http/router ["/" {:interceptors [api-chain my-interceptor]}] {:reitit.interceptor/transform custom-transform)Not possible. Because
into-interceptoris run on the interceptors vector once before the transformation (see(defn chain..) above). -
:compilei.e.,(http/router ["/" {:interceptors [api-chain my-interceptor]}] {:compile custom-compile-fn)
This is technically possible, but requires redefining:...oof 😨
-
One simple workaround of course is to:
["/" {:interceptors (conj api-chain my-interceptor) }]
Or for composing multiple chains (also very common):
["/" {:interceptors (concat api-chain auth-chain [my-interceptor]) }]
For experienced clojure developers the above two are of course perfectly serviceable, but a far cry from the readability of the following. When teaching new clojure devs about web dev, one of the first files they see is the routes file and the low-level noise of
concatis distracting from higher level concepts.Ideally we could write:
["/" {:interceptors [api-chain auth-chain my-interceptor])}] ;; where api-chain and auth-chain are vectors of IntoInterceptors, and my-interceptor is an IntoInterceptor
While I'm wishing, it would be even nicer if the api-chain (aka vectors of IntoInterceptors) could be put in the registry, allowing:
["/" {:interceptors [:my.proj/api-chain :my.auth/auth-chain my-interceptor])}]And I am pretty sure this would work out-of-the-box, if one IntoInterceptor could expand into multiple IntoInterceptors. Because registry is already defined as a map of keyword => IntoInterceptor.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status