diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d68907a..83e348f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,22 @@
* new optional module for [Pedestal](http://pedestal.io/) integration. See [the docs](https://metosin.github.io/reitit/http/pedestal.html).
+## `reitit-middleware`
+
+* `reitit.ring.middleware.dev/print-request-diffs` middleware transformation function to print out request diffs between middleware to the console
+ * read the [docs](https://metosin.github.io/reitit/ring/transforming_middleware_chain.html#printing-request-diffs)
+ * see [example app](https://github.com/metosin/reitit/tree/master/examples/ring-swagger)
+
+
+
+## `reitit-interceptors`
+
+* `reitit.http.interceptors.dev/print-context-diffs` interceptor transformation function to print out context diffs between interceptor steps to the console:
+ * read the [docs](https://metosin.github.io/reitit/http/transforming_interceptor_chain.html#printing-context-diffs)
+ * see [example app](https://github.com/metosin/reitit/tree/master/examples/http-swagger)
+
+
+
### dependencies
* updated:
diff --git a/doc/SUMMARY.md b/doc/SUMMARY.md
index 71bd208c..849725c6 100644
--- a/doc/SUMMARY.md
+++ b/doc/SUMMARY.md
@@ -44,6 +44,7 @@
* [Pedestal](http/pedestal.md)
* [Sieppari](http/sieppari.md)
* [Default Interceptors](http/default_interceptors.md)
+* [Transforming Interceptor Chain](http/transforming_interceptor_chain.md)
## Frontend
diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn
index 6e4014dd..7ba4c771 100644
--- a/doc/cljdoc.edn
+++ b/doc/cljdoc.edn
@@ -40,7 +40,8 @@
["Interceptors" {:file "doc/http/interceptors.md"}]
["Pedestal" {:file "doc/http/pedestal.md"}]
["Sieppari" {:file "doc/http/sieppari.md"}]
- ["Default Interceptors" {:file "doc/http/default_interceptors.md"}]]
+ ["Default Interceptors" {:file "doc/http/default_interceptors.md"}]
+ ["Transforming Interceptor Chain" {:file "doc/http/transforming_interceptor_chain.md"}]]
["Frontend" {}
["Basics" {:file "doc/frontend/basics.md"}]
["Browser integration" {:file "doc/frontend/browser.md"}]
diff --git a/doc/http/interceptors.md b/doc/http/interceptors.md
index 8e74d968..bcd1a0c4 100644
--- a/doc/http/interceptors.md
+++ b/doc/http/interceptors.md
@@ -12,9 +12,46 @@ An module for http-routing using interceptors instead of middleware. Builds on t
The differences:
-* instead of `:middleware`, uses `:interceptors`.
-* `reitit.http/http-router` requires an extra option `:executor` of type `reitit.interceptor/Executor`.
-* instead of creating a ring-handler, apps can be wrapped into a routing interceptor that enqueues the matched interceptors into the context. For this, there is `reitit.http/routing-interceptor`.
+* `:interceptors` key in used in route data instead of `:middleware`
+* `reitit.http/http-router` requires an extra option `:executor` of type `reitit.interceptor/Executor` to execute the interceptor chain
+ * optionally, a routing interceptor can be used - it enqueues the matched interceptors into the context. See `reitit.http/routing-interceptor` for details.
+
+## Simple example
+
+```clj
+(require '[reitit.ring :as ring])
+(require '[reitit.http :as http])
+(require '[reitit.interceptor.sieppari :as sieppari])
+
+(defn interceptor [number]
+ {:enter (fn [ctx] (update-in ctx [:request :number] (fnil + 0) number))})
+
+(def app
+ (http/ring-handler
+ (http/router
+ ["/api"
+ {:interceptors [(interceptor 1)]}
+
+ ["/number"
+ {:interceptors [(interceptor 10)]
+ :get {:interceptors [(interceptor 100)]
+ :handler (fn [req]
+ {:status 200
+ :body (select-keys req [:number])})}}]])
+
+ ;; the default handler
+ (ring/create-default-handler)
+
+ ;; executor
+ {:executor sieppari/executor}))
+
+
+(app {:request-method :get, :uri "/"})
+; {:status 404, :body "", :headers {}}
+
+(app {:request-method :get, :uri "/api/number"})
+; {:status 200, :body {:number 111}}
+```
## Why interceptors?
diff --git a/doc/http/pedestal.md b/doc/http/pedestal.md
index e0b441ce..be1efdc4 100644
--- a/doc/http/pedestal.md
+++ b/doc/http/pedestal.md
@@ -29,14 +29,24 @@ A minimalistic example on how to to swap the default-router with a reitit router
; [metosin/reitit-pedestal "0.2.10-alpha1"]
; [metosin/reitit "0.2.10-alpha1"]
-(ns example.server
- (:require [io.pedestal.http :as server]
- [reitit.pedestal :as pedestal]
- [reitit.http :as http]
- [reitit.ring :as ring]))
+(require '[io.pedestal.http :as server])
+(require '[reitit.pedestal :as pedestal])
+(require '[reitit.http :as http])
+(require '[reitit.ring :as ring])
+
+(defn interceptor [number]
+ {:enter (fn [ctx] (update-in ctx [:request :number] (fnil + 0) number))})
(def routes
- ["/ping" {:get (fn [_] {:status 200, :body "pong"})}])
+ ["/api"
+ {:interceptors [(interceptor 1)]}
+
+ ["/number"
+ {:interceptors [(interceptor 10)]
+ :get {:interceptors [(interceptor 100)]
+ :handler (fn [req]
+ {:status 200
+ :body (select-keys req [:number])})}}]])
(-> {::server/type :jetty
::server/port 3000
diff --git a/doc/http/transforming_interceptor_chain.md b/doc/http/transforming_interceptor_chain.md
new file mode 100644
index 00000000..ca380b72
--- /dev/null
+++ b/doc/http/transforming_interceptor_chain.md
@@ -0,0 +1,79 @@
+# Transforming the Interceptor Chain
+
+There is an extra option in http-router (actually, in the underlying interceptor-router): `:reitit.interceptor/transform` to transform the interceptor chain per endpoint. Value should be a function or a vector of functions that get a vector of compiled interceptors and should return a new vector of interceptors.
+
+**Note:** the last interceptor in the chain is usually the handler, compiled into an Interceptor. Applying a tranfromation `clojure.core/reverse` would put this interceptor into first in the chain, making the rest of the interceptors effectively unreachable. There is a helper `reitit.interceptor/transform-butlast` to transform all but the last interceptor.
+
+## Example Application
+
+```clj
+(require '[reitit.http :as http])
+(require '[reitit.interceptor.sieppari :as sieppari])
+
+(defn interceptor [message]
+ {:enter (fn [ctx] (update-in ctx [:request :message] (fnil conj []) message))})
+
+(defn handler [req]
+ {:status 200
+ :body (select-keys req [:message])})
+
+(def app
+ (http/ring-handler
+ (http/router
+ ["/api" {:interceptors [(interceptor 1) (interceptor 2)]}
+ ["/ping" {:get {:interceptors [(interceptor 3)]
+ :handler handler}}]])
+ {:executor sieppari/executor}))
+
+(app {:request-method :get, :uri "/api/ping"})
+; {:status 200, :body {:message [1 2 3]}}
+
+```
+
+### Reversing the Interceptor Chain
+
+```clj
+(def app
+ (http/ring-handler
+ (http/router
+ ["/api" {:interceptors [(interceptor 1) (interceptor 2)]}
+ ["/ping" {:get {:interceptors [(interceptor 3)]
+ :handler handler}}]]
+ {::interceptor/transform (interceptor/transform-butlast reverse)})
+ {:executor sieppari/executor}))
+
+(app {:request-method :get, :uri "/api/ping"})
+; {:status 200, :body {:message [3 2 1]}}
+```
+
+### Interleaving Interceptors
+
+```clj
+(def app
+ (http/ring-handler
+ (http/router
+ ["/api" {:interceptors [(interceptor 1) (interceptor 2)]}
+ ["/ping" {:get {:interceptors [(interceptor 3)]
+ :handler handler}}]]
+ {::interceptor/transform #(interleave % (repeat (interceptor :debug)))})
+ {:executor sieppari/executor}))
+
+(app {:request-method :get, :uri "/api/ping"})
+; {:status 200, :body {:message [1 :debug 2 :debug 3 :debug]}}
+```
+
+### Printing Context Diffs
+
+```clj
+[metosin/reitit-interceptors "0.2.9"]
+```
+
+Using `reitit.http.interceptors.dev/print-context-diffs` transformation, the context diffs between each interceptor are printed out to the console. To use it, add the following router option:
+
+```clj
+:reitit.interceptor/transform reitit.http.interceptor.dev/print-context-diffs
+```
+
+Sample output:
+
+
diff --git a/doc/images/http-context-diff.png b/doc/images/http-context-diff.png
new file mode 100644
index 00000000..88c07ece
Binary files /dev/null and b/doc/images/http-context-diff.png differ
diff --git a/doc/images/ring-request-diff.png b/doc/images/ring-request-diff.png
new file mode 100644
index 00000000..a7973e60
Binary files /dev/null and b/doc/images/ring-request-diff.png differ
diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md
index 018739b9..dbdb80a6 100644
--- a/doc/ring/default_middleware.md
+++ b/doc/ring/default_middleware.md
@@ -6,19 +6,20 @@
Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases, yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware.
-* [Parameter handling](#parameters-handling)
-* [Exception handling](#exception-handling)
-* [Content negotiation](#content-negotiation)
-* [Multipart request handling](#multipart-request-handling)
+* [Parameter Handling](#parameters-handling)
+* [Exception Handling](#exception-handling)
+* [Content Negotiation](#content-negotiation)
+* [Multipart Request Handling](#multipart-request-handling)
+* [Inspecting Requests](#inspecting-requests)
-## Parameters handling
+## Parameters Handling
`reitit.ring.middleware.parameters/parameters-middleware` to capture query- and form-params. Wraps
`ring.middleware.params/wrap-params`.
**NOTE**: will be factored into two parts: a query-parameters middleware and a Muuntaja format responsible for the the `application/x-www-form-urlencoded` body format.
-## Exception handling
+## Exception Handling
A polished version of [compojure-api](https://github.com/metosin/compojure-api) exception handling. Catches all exceptions and invokes configured exception handler.
@@ -207,7 +208,7 @@ Server: Jetty(9.2.21.v20170120)
kukka
```
-## Multipart request handling
+## Multipart Request Handling
Wrapper for [Ring Multipart Middleware](https://github.com/ring-clojure/ring/blob/master/ring-core/src/ring/middleware/multipart_params.clj). Emits swagger `:consumes` definitions automatically.
@@ -225,6 +226,18 @@ Expected route data:
* `multipart/multipart-middleware` a preconfigured middleware for multipart handling
* `multipart/create-multipart-middleware` to generate with custom configuration
+## Inspecting Requests
+
+`reitit.ring.middleware.dev/print-request-diffs` is a [middleware chain transforming function](transforming_middleware_chain.md). It prints a request diff between each middleware. To use it, add the following router option:
+
+```clj
+:reitit.middleware/transform reitit.ring.middleware.dev/print-request-diffs
+```
+
+Partial sample output:
+
+
+
## Example app
See an example app with the default middleware in action: https://github.com/metosin/reitit/blob/master/examples/ring-swagger/src/example/server.clj.
diff --git a/doc/ring/transforming_middleware_chain.md b/doc/ring/transforming_middleware_chain.md
index 55d60d08..9dcff0e1 100644
--- a/doc/ring/transforming_middleware_chain.md
+++ b/doc/ring/transforming_middleware_chain.md
@@ -1,37 +1,74 @@
# Transforming the Middleware Chain
-There is an extra option in ring-router (actually, in the underlying middleware-router): `:reitit.middleware/transform` to transform the middleware chain per endpoint. It gets the vector of compiled middleware and should return a new vector of middleware.
+There is an extra option in ring-router (actually, in the underlying middleware-router): `:reitit.middleware/transform` to transform the middleware chain per endpoint. Value should be a function or a vector of functions that get a vector of compiled middleware and should return a new vector of middleware.
-## Adding debug middleware between all other middleware
+## Example Application
+
+```clj
+(require '[reitit.ring :as ring])
+(require '[reitit.middleware :as middleware])
+
+(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 [[wrap 1] [wrap 2]]}
+ ["/ping" {:get {:middleware [[wrap 3]]
+ :handler handler}}]])))
+
+(app {:request-method :get, :uri "/api/ping"})
+; {:status 200, :body [1 2 3 :handler]}
+```
+
+### Reversing the Middleware Chain
```clj
(def app
(ring/ring-handler
(ring/router
- ["/api" {:middleware [[wrap 1] [wrap2 2]]}
- ["/ping" {:get {:middleware [[wrap3 3]]
+ ["/api" {:middleware [[wrap 1] [wrap 2]]}
+ ["/ping" {:get {:middleware [[wrap 3]]
:handler handler}}]]
- {::middleware/transform #(interleave % (repeat [wrap :debug]))})))
+ {::middleware/transform reverse})))
+
+(app {:request-method :get, :uri "/api/ping"})
+; {:status 200, :body [3 2 1 :handler]}
```
+## Interleaving Middleware
+
```clj
+(def app
+ (ring/ring-handler
+ (ring/router
+ ["/api" {:middleware [[wrap 1] [wrap 2]]}
+ ["/ping" {:get {:middleware [[wrap 3]]
+ :handler handler}}]]
+ {::middleware/transform #(interleave % (repeat [wrap :debug]))})))
+
(app {:request-method :get, :uri "/api/ping"})
; {:status 200, :body [1 :debug 2 :debug 3 :debug :handler]}
```
-## Reversing the middleware chain
+### Printing Request Diffs
```clj
-(def app
- (ring/ring-handler
- (ring/router
- ["/api" {:middleware [[wrap 1] [wrap2 2]]}
- ["/ping" {:get {:middleware [[wrap3 3]]
- :handler handler}}]]
- {::middleware/transform reverse)})))
+[metosin/reitit-middleware "0.2.9"]
```
+Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the request diffs between each middleware are printed out to the console. To use it, add the following router option:
+
```clj
-(app {:request-method :get, :uri "/api/ping"})
-; {:status 200, :body [3 2 1 :handler]}
+:reitit.middleware/transform reitit.ring.middleware.dev/print-request-diffs
```
+
+Sample output:
+
+
+
diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj
index eb84bd73..81b8ede4 100644
--- a/examples/http-swagger/src/example/server.clj
+++ b/examples/http-swagger/src/example/server.clj
@@ -109,7 +109,7 @@
{:status 200
:body {:total (- x y)}})}}]]]
- {;:reitit.interceptor/transform dev/print-request-diffs
+ {;;:reitit.interceptor/transform dev/print-context-diffs
:data {:coercion spec-coercion/coercion
:muuntaja m/instance
:interceptors [;; query-params & form-params
diff --git a/examples/pedestal-swagger/src/example/server.clj b/examples/pedestal-swagger/src/example/server.clj
index af99e8d3..51e16059 100644
--- a/examples/pedestal-swagger/src/example/server.clj
+++ b/examples/pedestal-swagger/src/example/server.clj
@@ -10,6 +10,7 @@
[reitit.http.interceptors.parameters :as parameters]
[reitit.http.interceptors.muuntaja :as muuntaja]
[reitit.http.interceptors.multipart :as multipart]
+ [reitit.http.interceptors.dev :as dev]
[clojure.core.async :as a]
[clojure.java.io :as io]
[muuntaja.core :as m]))
@@ -75,7 +76,8 @@
{:status 200
:body {:total (+ x y)}})}}]]]
- {:data {:coercion spec-coercion/coercion
+ {;;:reitit.interceptor/transform dev/print-context-diffs
+ :data {:coercion spec-coercion/coercion
:muuntaja m/instance
:interceptors [;; query-params & form-params
(parameters/parameters-interceptor)
diff --git a/modules/reitit-core/src/reitit/interceptor.cljc b/modules/reitit-core/src/reitit/interceptor.cljc
index 1a2ff1fa..e3d4f3c9 100644
--- a/modules/reitit-core/src/reitit/interceptor.cljc
+++ b/modules/reitit-core/src/reitit/interceptor.cljc
@@ -127,6 +127,15 @@
:queue ((or queue identity) chain)
:data data}))))
+(defn transform-butlast
+ "Returns a function to that takes a interceptor transformation function and
+ transforms all but last of the interceptors (e.g. the handler)"
+ [f]
+ (fn [interceptors]
+ (concat
+ (f (butlast interceptors))
+ [(last interceptors)])))
+
(defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
support for Interceptors. See documentation on [[reitit.core/router]] for available options.
diff --git a/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj b/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj
index 096f9c08..bd652885 100644
--- a/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj
+++ b/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj
@@ -33,18 +33,19 @@
(assoc ::previous ctx)))))
(defn diff-interceptor
- [stages {:keys [enter leave error name]}]
- (cond-> {:name ::diff}
- (and enter (stages :enter)) (assoc :enter (handle name :enter))
- (and leave (stages :leave)) (assoc :leave (handle name :leave))
- (and error (stages :error)) (assoc :error (handle name :error))))
+ [stages {:keys [enter leave error name] :as interceptor}]
+ (if (->> (select-keys interceptor stages) (vals) (keep identity) (seq))
+ (cond-> {:name ::diff}
+ (and enter (stages :enter)) (assoc :enter (handle name :enter))
+ (and leave (stages :leave)) (assoc :leave (handle name :leave))
+ (and error (stages :error)) (assoc :error (handle name :error)))))
(defn print-context-diffs
"A interceptor chain transformer that adds a context diff printer between all interceptors"
- [chain]
+ [interceptors]
(reduce
(fn [chain interceptor]
- (into chain [(diff-interceptor #{:leave :error} interceptor)
- interceptor
- (diff-interceptor #{:enter} interceptor)]))
- [(diff-interceptor #{:enter :leave :error} {:enter identity})] chain))
+ (into chain (keep identity [(diff-interceptor #{:leave :error} interceptor)
+ interceptor
+ (diff-interceptor #{:enter} interceptor)])))
+ [(diff-interceptor #{:enter :leave :error} {:enter identity})] interceptors))
diff --git a/test/cljc/reitit/interceptor_test.cljc b/test/cljc/reitit/interceptor_test.cljc
index 3eed9ae7..8071e48f 100644
--- a/test/cljc/reitit/interceptor_test.cljc
+++ b/test/cljc/reitit/interceptor_test.cljc
@@ -223,14 +223,8 @@
(enter ::avaruus)]
:handler handler}]
options)))
- inject-debug (fn [interceptors]
- (concat
- (interleave (butlast interceptors) (repeat debug-i))
- [(last interceptors)]))
- sort-interceptors (fn [interceptors]
- (concat
- (sort-by :name (butlast interceptors))
- [(last interceptors)]))]
+ inject-debug (interceptor/transform-butlast #(interleave % (repeat debug-i)))
+ sort-interceptors (interceptor/transform-butlast (partial sort-by :name))]
(testing "by default, all interceptors are applied in order"
(let [app (create nil)]