From 158695d47b484d28c0331f21d0c1de3acd7ab065 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 22 Sep 2018 17:21:25 +0300 Subject: [PATCH 1/3] Support for top-level middleware in reitit-ring See #143 --- modules/reitit-ring/src/reitit/ring.cljc | 68 +++++++++++++----------- test/cljc/reitit/ring_test.cljc | 16 ++++++ 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index f74b0e85..3b08e113 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -177,41 +177,47 @@ (create handler))))) (defn ring-handler - "Creates a ring-handler out of a ring-router. - Supports both 1 (sync) and 3 (async) arities. - Optionally takes a ring-handler which is called - in no route matches." + "Creates a ring-handler out of a router, optional default ring-handler + and options map, with the following keys: + + | key | description | + | --------------|-------------| + | `:middleware` | Optional sequence of middleware that are wrap the [[ring-handler]]" ([router] (ring-handler router nil)) ([router default-handler] - (let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil))))] + (ring-handler router default-handler nil)) + ([router default-handler {:keys [middleware]}] + (let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil)))) + wrap (if middleware (partial middleware/chain middleware) identity)] (with-meta - (fn - ([request] - (if-let [match (r/match-by-path router (:uri request))] - (let [method (:request-method request) - path-params (:path-params match) - result (:result match) - handler (-> result method :handler (or default-handler)) - request (-> request - (impl/fast-assoc :path-params path-params) - (impl/fast-assoc ::r/match match) - (impl/fast-assoc ::r/router router))] - (or (handler request) (default-handler request))) - (default-handler request))) - ([request respond raise] - (if-let [match (r/match-by-path router (:uri request))] - (let [method (:request-method request) - path-params (:path-params match) - result (:result match) - handler (-> result method :handler (or default-handler)) - request (-> request - (impl/fast-assoc :path-params path-params) - (impl/fast-assoc ::r/match match) - (impl/fast-assoc ::r/router router))] - ((routes handler default-handler) request respond raise)) - (default-handler request respond raise)) - nil)) + (wrap + (fn + ([request] + (if-let [match (r/match-by-path router (:uri request))] + (let [method (:request-method request) + path-params (:path-params match) + result (:result match) + handler (-> result method :handler (or default-handler)) + request (-> request + (impl/fast-assoc :path-params path-params) + (impl/fast-assoc ::r/match match) + (impl/fast-assoc ::r/router router))] + (or (handler request) (default-handler request))) + (default-handler request))) + ([request respond raise] + (if-let [match (r/match-by-path router (:uri request))] + (let [method (:request-method request) + path-params (:path-params match) + result (:result match) + handler (-> result method :handler (or default-handler)) + request (-> request + (impl/fast-assoc :path-params path-params) + (impl/fast-assoc ::r/match match) + (impl/fast-assoc ::r/router router))] + ((routes handler default-handler) request respond raise)) + (default-handler request respond raise)) + nil))) {::r/router router})))) (defn get-router [handler] diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index 60bdb2e4..2038855d 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -78,6 +78,22 @@ (is (= {:status 200, :body [:api :users :post :ok]} @result)))))) + (testing "with top-level middleware" + (let [router (ring/router + ["/api" {:middleware [[mw :api]]} + ["/get" {:get handler}]]) + app (ring/ring-handler router nil {:middleware [[mw :top]]})] + + (testing "router can be extracted" + (is (= router (ring/get-router app)))) + + (testing "not found" + (is (= nil (app {:uri "/favicon.ico"})))) + + (testing "on match" + (is (= {:status 200, :body [:top :api :ok]} + (app {:uri "/api/get" :request-method :get})))))) + (testing "named routes" (let [router (ring/router [["/api" From 0de842585fd896205e022b330a51dd110cc24554 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 22 Sep 2018 21:41:54 +0300 Subject: [PATCH 2/3] add docs --- doc/ring/ring.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/ring/ring.md b/doc/ring/ring.md index 80297c3b..340a1f2b 100644 --- a/doc/ring/ring.md +++ b/doc/ring/ring.md @@ -127,6 +127,21 @@ Middleware is applied correctly: ; {:status 200, :body [:api :admin :db :delete :handler]} ``` +Top-level middleware, applied before any routing is done: + +```clj +(def app + (ring/ring-handler + (ring/router + ["/api" {:middleware [[mw :api]]} + ["/get" {:get handler}]]) + nil + {:middleware [[mw :top]]})) + +(app {:request-method :get, :uri "/api/get"}) +; {:status 200, :body [:top :api :ok]} +``` + # Async Ring All built-in middleware provide both 2 and 3-arity and are compiled for both Clojure & ClojureScript, so they work with [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) and [Node.js](https://nodejs.org) too. From 2d71b37246cd7524630a1d1786d6b820a4faf287 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 22 Sep 2018 21:47:55 +0300 Subject: [PATCH 3/3] CHANGELOG --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ddee3e9..24c725c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,35 @@ ## `reitit-ring` +* `ring-handler` takes optionally a 3rd argument, an options map which can be used to se top-level middleware, applied before any routing is done: + +```clj +(require '[reitit.ring :as ring]) + +(defn wrap [handler id] + (fn [request] + (handler (update request ::acc (fnil conj []) id)))) + +(defn handler [{:keys [::acc]}] + {:status 200, :body (conj acc :handler)}) + +(def app + (ring/ring-handler + (ring/router + ["/api" {:middleware [[mw :api]]} + ["/get" {:get handler}]]) + (ring/create-default-handler) + {:middleware [[mw :top]]})) + +(app {:request-method :get, :uri "/api/get"}) +; {:status 200, :body [:top :api :ok]} + +(require '[reitit.core :as r]) + +(-> app (ring/get-router)) +; #object[reitit.core$single_static_path_router] +``` + * updated deps: ```clj