mirror of
https://github.com/metosin/reitit.git
synced 2025-12-16 16:01:11 +00:00
Polish docs
This commit is contained in:
parent
07fa785df3
commit
2ec29b8f9d
15 changed files with 252 additions and 52 deletions
16
CHANGELOG.md
16
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)
|
||||
|
||||
<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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"}]
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
79
doc/http/transforming_interceptor_chain.md
Normal file
79
doc/http/transforming_interceptor_chain.md
Normal 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:
|
||||
|
||||

|
||||
BIN
doc/images/http-context-diff.png
Normal file
BIN
doc/images/http-context-diff.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 328 KiB |
BIN
doc/images/ring-request-diff.png
Normal file
BIN
doc/images/ring-request-diff.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 235 KiB |
|
|
@ -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:
|
||||
|
||||

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

|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue