Polish docs

This commit is contained in:
Tommi Reiman 2018-12-30 16:53:15 +02:00
parent 07fa785df3
commit 2ec29b8f9d
15 changed files with 252 additions and 52 deletions

View file

@ -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)
<img src="https://metosin.github.io/reitit/images/ring-request-diff.png" width=320>
## `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)
<img src="https://metosin.github.io/reitit/images/http-context-diff.png" width=320>
### dependencies
* updated:

View file

@ -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

View file

@ -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"}]

View file

@ -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?

View file

@ -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

View file

@ -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:
![Http Context Diff](../images/http-context-diff.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

View file

@ -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)
<kikka>kukka</kikka>
```
## 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:
![Opensensors perf test](../images/ring-request-diff.png)
## 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.

View file

@ -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:
![Ring Request Diff](../images/ring-request-diff.png)

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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))

View file

@ -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)]