mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 17:01:11 +00:00
:gel-wrap => :compile
This commit is contained in:
parent
e56dc4ef70
commit
164473cc75
7 changed files with 51 additions and 51 deletions
|
|
@ -4,9 +4,9 @@ The [dynamic extensions](dynamic_extensions.md) is a easy way to extend the syst
|
||||||
|
|
||||||
But, we can do much better. As we know the exact route that middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware/interceptor at creation-time. It can do local reasoning: extract and transform relevant data just for it and pass it into the actual request-handler via a closure - yielding much faster runtime processing. It can also decide not to mount itself by returning `nil`. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it?
|
But, we can do much better. As we know the exact route that middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware/interceptor at creation-time. It can do local reasoning: extract and transform relevant data just for it and pass it into the actual request-handler via a closure - yielding much faster runtime processing. It can also decide not to mount itself by returning `nil`. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it?
|
||||||
|
|
||||||
To enable this we use [middleware records](data_driven_middleware.md) `:gen-wrap` key instead of the normal `:wrap`. `:gen-wrap` expects a function of `route-data router-opts => ?wrap`.
|
To enable this we use [middleware records](data_driven_middleware.md) `:compile` key instead of the normal `:wrap`. `:compile` expects a function of `route-data router-opts => ?wrap`.
|
||||||
|
|
||||||
To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:gen-wrap`.
|
To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:compile`.
|
||||||
|
|
||||||
## Normal Middleware
|
## Normal Middleware
|
||||||
|
|
||||||
|
|
@ -57,15 +57,15 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
||||||
and :responses from route data, otherwise does not mount."
|
and :responses from route data, otherwise does not mount."
|
||||||
(middleware/create
|
(middleware/create
|
||||||
{:name ::coerce-response
|
{:name ::coerce-response
|
||||||
:gen-wrap (fn [{:keys [coercion responses opts]} _]
|
:compile (fn [{:keys [coercion responses opts]} _]
|
||||||
(if (and coercion responses)
|
(if (and coercion responses)
|
||||||
(let [coercers (response-coercers coercion responses opts)]
|
(let [coercers (response-coercers coercion responses opts)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
(coerce-response coercers request (handler request)))
|
(coerce-response coercers request (handler request)))
|
||||||
([request respond raise]
|
([request respond raise]
|
||||||
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
||||||
```
|
```
|
||||||
|
|
||||||
The latter has 50% less code, is easier to reason about and is much faster.
|
The latter has 50% less code, is easier to reason about and is much faster.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ Records can have arbitrary keys, but the following keys have a special purpose:
|
||||||
| ---------------|-------------|
|
| ---------------|-------------|
|
||||||
| `:name` | Name of the middleware as a qualified keyword (optional)
|
| `:name` | Name of the middleware as a qualified keyword (optional)
|
||||||
| `:wrap` | The actual middleware function of `handler & args => request => response`
|
| `:wrap` | The actual middleware function of `handler & args => request => response`
|
||||||
| `:gen-wrap` | Middleware function generation function, see [compiling middleware](compiling_middleware.md).
|
| `:compile` | Middleware function generation function, see [compiling middleware](compiling_middleware.md).
|
||||||
|
|
||||||
Middleware Records are accessible in their raw form in the compiled route results, thus available for inventories, creating api-docs etc.
|
Middleware Records are accessible in their raw form in the compiled route results, thus available for inventories, creating api-docs etc.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,3 +153,6 @@
|
||||||
(defn fast-get
|
(defn fast-get
|
||||||
#?@(:clj [[^java.util.HashMap m k] (.get m k)]
|
#?@(:clj [[^java.util.HashMap m k] (.get m k)]
|
||||||
:cljs [[m k] (m k)]))
|
:cljs [[m k] (m k)]))
|
||||||
|
|
||||||
|
(defn strip-nils [m]
|
||||||
|
(->> m (remove (comp nil? second)) (into {})))
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
and :parameters from route data, otherwise does not mount."
|
and :parameters from route data, otherwise does not mount."
|
||||||
(middleware/create
|
(middleware/create
|
||||||
{:name ::coerce-parameters
|
{:name ::coerce-parameters
|
||||||
:gen-wrap (fn [{:keys [coercion parameters]} opts]
|
:compile (fn [{:keys [coercion parameters]} opts]
|
||||||
(if (and coercion parameters)
|
(if (and coercion parameters)
|
||||||
(let [coercers (request-coercers coercion parameters opts)]
|
(let [coercers (request-coercers coercion parameters opts)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
|
|
@ -143,7 +143,7 @@
|
||||||
and :responses from route data, otherwise does not mount."
|
and :responses from route data, otherwise does not mount."
|
||||||
(middleware/create
|
(middleware/create
|
||||||
{:name ::coerce-response
|
{:name ::coerce-response
|
||||||
:gen-wrap (fn [{:keys [coercion responses]} opts]
|
:compile (fn [{:keys [coercion responses]} opts]
|
||||||
(if (and coercion responses)
|
(if (and coercion responses)
|
||||||
(let [coercers (response-coercers coercion responses opts)]
|
(let [coercers (response-coercers coercion responses opts)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
|
|
@ -159,7 +159,7 @@
|
||||||
and :parameters or :responses from route data, otherwise does not mount."
|
and :parameters or :responses from route data, otherwise does not mount."
|
||||||
(middleware/create
|
(middleware/create
|
||||||
{:name ::coerce-exceptions
|
{:name ::coerce-exceptions
|
||||||
:gen-wrap (fn [{:keys [coercion parameters responses]} _]
|
:compile (fn [{:keys [coercion parameters responses]} _]
|
||||||
(if (and coercion (or parameters responses))
|
(if (and coercion (or parameters responses))
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
(fn
|
(fn
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
(ns reitit.ring.middleware
|
(ns reitit.ring.middleware
|
||||||
(:require [meta-merge.core :refer [meta-merge]]
|
(:require [meta-merge.core :refer [meta-merge]]
|
||||||
[reitit.core :as r]))
|
[reitit.core :as r]
|
||||||
|
[reitit.impl :as impl]))
|
||||||
|
|
||||||
(defprotocol IntoMiddleware
|
(defprotocol IntoMiddleware
|
||||||
(into-middleware [this data opts]))
|
(into-middleware [this data opts]))
|
||||||
|
|
@ -8,11 +9,11 @@
|
||||||
(defrecord Middleware [name wrap])
|
(defrecord Middleware [name wrap])
|
||||||
(defrecord Endpoint [data handler middleware])
|
(defrecord Endpoint [data handler middleware])
|
||||||
|
|
||||||
(defn create [{:keys [name wrap gen-wrap] :as m}]
|
(defn create [{:keys [name wrap compile] :as m}]
|
||||||
(when (and wrap gen-wrap)
|
(when (and wrap compile)
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Middleware can't both :wrap and :gen-wrap defined " m) m)))
|
(str "Middleware can't both :wrap and :compile defined " m) m)))
|
||||||
(map->Middleware m))
|
(map->Middleware m))
|
||||||
|
|
||||||
(extend-protocol IntoMiddleware
|
(extend-protocol IntoMiddleware
|
||||||
|
|
@ -40,14 +41,14 @@
|
||||||
(into-middleware (create this) data opts))
|
(into-middleware (create this) data opts))
|
||||||
|
|
||||||
Middleware
|
Middleware
|
||||||
(into-middleware [{:keys [wrap gen-wrap] :as this} data opts]
|
(into-middleware [{:keys [wrap compile] :as this} data opts]
|
||||||
(if-not gen-wrap
|
(if-not compile
|
||||||
this
|
this
|
||||||
(if-let [wrap (gen-wrap data opts)]
|
(if-let [middeware (into-middleware (compile data opts) data opts)]
|
||||||
(map->Middleware
|
(map->Middleware
|
||||||
(-> this
|
(merge
|
||||||
(dissoc :gen-wrap)
|
(dissoc this :create)
|
||||||
(assoc :wrap wrap))))))
|
(impl/strip-nils middeware))))))
|
||||||
|
|
||||||
nil
|
nil
|
||||||
(into-middleware [_ _ _]))
|
(into-middleware [_ _ _]))
|
||||||
|
|
@ -68,13 +69,6 @@
|
||||||
(defn compile-handler [middleware handler]
|
(defn compile-handler [middleware handler]
|
||||||
((apply comp identity (keep :wrap middleware)) handler))
|
((apply comp identity (keep :wrap middleware)) handler))
|
||||||
|
|
||||||
(compile-handler
|
|
||||||
[(map->Middleware
|
|
||||||
{:wrap
|
|
||||||
(fn [handler]
|
|
||||||
(fn [request]
|
|
||||||
(handler request)))})] identity)
|
|
||||||
|
|
||||||
(defn compile-result
|
(defn compile-result
|
||||||
([route opts]
|
([route opts]
|
||||||
(compile-result route opts nil))
|
(compile-result route opts nil))
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,6 @@
|
||||||
(into [] (impl/segments "/api/ipa/beer/craft/bisse"))))
|
(into [] (impl/segments "/api/ipa/beer/craft/bisse"))))
|
||||||
(is (= ["" "a" "" "b" "" "c" ""]
|
(is (= ["" "a" "" "b" "" "c" ""]
|
||||||
(into [] (impl/segments "/a//b//c/")))))
|
(into [] (impl/segments "/a//b//c/")))))
|
||||||
|
|
||||||
|
(deftest strip-nils-test
|
||||||
|
(is (= {:a 1, :c false} (impl/strip-nils {:a 1, :b nil, :c false}))))
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@
|
||||||
|
|
||||||
(testing "middleware records"
|
(testing "middleware records"
|
||||||
|
|
||||||
(testing ":wrap & :gen-wrap are exclusive"
|
(testing ":wrap & :compile are exclusive"
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
#"Middleware can't both :wrap and :gen-wrap defined"
|
#"Middleware can't both :wrap and :compile defined"
|
||||||
(middleware/create
|
(middleware/create
|
||||||
{:name ::test
|
{:name ::test
|
||||||
:wrap identity
|
:wrap identity
|
||||||
:gen-wrap (constantly identity)}))))
|
:compile (constantly identity)}))))
|
||||||
|
|
||||||
(testing "middleware"
|
(testing "middleware"
|
||||||
(let [calls (atom 0)
|
(let [calls (atom 0)
|
||||||
|
|
@ -74,12 +74,12 @@
|
||||||
|
|
||||||
(testing "compiled Middleware"
|
(testing "compiled Middleware"
|
||||||
(let [calls (atom 0)
|
(let [calls (atom 0)
|
||||||
mw {:gen-wrap (fn [data _]
|
mw {:compile (fn [data _]
|
||||||
(swap! calls inc)
|
(swap! calls inc)
|
||||||
(fn [handler value]
|
(fn [handler value]
|
||||||
(swap! calls inc)
|
(swap! calls inc)
|
||||||
(fn [request]
|
(fn [request]
|
||||||
[data value request])))}
|
[data value request])))}
|
||||||
->app (fn [ast handler]
|
->app (fn [ast handler]
|
||||||
(middleware/compile-handler
|
(middleware/compile-handler
|
||||||
(middleware/expand ast :data {})
|
(middleware/expand ast :data {})
|
||||||
|
|
@ -100,8 +100,8 @@
|
||||||
(is (= 2 @calls)))))
|
(is (= 2 @calls)))))
|
||||||
|
|
||||||
(testing "nil unmounts the middleware"
|
(testing "nil unmounts the middleware"
|
||||||
(let [app (->app [{:gen-wrap (constantly nil)}
|
(let [app (->app [{:compile (constantly nil)}
|
||||||
{:gen-wrap (constantly nil)}] identity)]
|
{:compile (constantly nil)}] identity)]
|
||||||
(dotimes [_ 10]
|
(dotimes [_ 10]
|
||||||
(is (= :request (app :request))))))))))
|
(is (= :request (app :request))))))))))
|
||||||
|
|
||||||
|
|
@ -145,9 +145,9 @@
|
||||||
(testing "with nested middleware"
|
(testing "with nested middleware"
|
||||||
(is (= [:api :admin :ok :admin :api] (app "/api/admin/ping"))))
|
(is (= [:api :admin :ok :admin :api] (app "/api/admin/ping"))))
|
||||||
|
|
||||||
(testing ":gen-wrap middleware can be unmounted at creation-time"
|
(testing ":compile middleware can be unmounted at creation-time"
|
||||||
(let [mw1 {:name ::mw1, :gen-wrap (constantly #(mw % ::mw1))}
|
(let [mw1 {:name ::mw1, :compile (constantly #(mw % ::mw1))}
|
||||||
mw2 {:name ::mw2, :gen-wrap (constantly nil)}
|
mw2 {:name ::mw2, :compile (constantly nil)}
|
||||||
mw3 {:name ::mw3, :wrap #(mw % ::mw3)}
|
mw3 {:name ::mw3, :wrap #(mw % ::mw3)}
|
||||||
router (middleware/router
|
router (middleware/router
|
||||||
["/api" {:name ::api
|
["/api" {:name ::api
|
||||||
|
|
@ -176,13 +176,13 @@
|
||||||
(let [mw (fn [handler value]
|
(let [mw (fn [handler value]
|
||||||
#(conj (handler (conj % value)) value))
|
#(conj (handler (conj % value)) value))
|
||||||
handler #(conj % :ok)
|
handler #(conj % :ok)
|
||||||
mw1 {:gen-wrap (constantly #(mw % ::mw1))}
|
mw1 {:compile (constantly #(mw % ::mw1))}
|
||||||
mw2 {:gen-wrap (constantly nil)}
|
mw2 {:compile (constantly nil)}
|
||||||
mw3 {:wrap #(mw % ::mw3)}
|
mw3 {:wrap #(mw % ::mw3)}
|
||||||
mw4 #(mw % ::mw4)
|
mw4 #(mw % ::mw4)
|
||||||
mw5 {:gen-wrap (fn [{:keys [mount?]} _]
|
mw5 {:compile (fn [{:keys [mount?]} _]
|
||||||
(when mount?
|
(when mount?
|
||||||
#(mw % ::mw5)))}
|
#(mw % ::mw5)))}
|
||||||
chain1 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? true})
|
chain1 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? true})
|
||||||
chain2 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? false})]
|
chain2 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? false})]
|
||||||
(is (= [::mw1 ::mw3 ::mw4 ::mw5 :ok ::mw5 ::mw4 ::mw3 ::mw1] (chain1 [])))
|
(is (= [::mw1 ::mw3 ::mw4 ::mw5 :ok ::mw5 ::mw4 ::mw3 ::mw1] (chain1 [])))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue