Update docs

This commit is contained in:
Tommi Reiman 2017-12-04 08:36:11 +02:00
parent ba78008d90
commit 33260396d2

View file

@ -1,23 +1,25 @@
# Data-driven Middleware
Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It's easy to undrstand and enables great performance. Still, in the end - the middleware-chain is just a opaque function, making things like documentation and debugging hard.
Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It's relatively easy to understand and enables good performance. Downside is that the middleware-chain is just a opaque function, making things like debugging and composition hard. It's too easy to apply the middleware in wrong order.
Reitit does things bit differently:
Reitit defines middleware as data:
1. Middleware is defined as a vector (of middleware) enabling the chain to be malipulated before turned into the runtime middleware function.
2. Middleware can be defined as first-class data entries
1. Middleware can be defined as first-class data entries
2. Middleware can be defined as a [duct-style](https://github.com/duct-framework/duct/wiki/Configuration) vector (of middleware)
4. Middleware can be optimized & [compiled](compiling_middleware.md) againt an endpoint
3. Middleware chain can be transformed by the router
### Middleware as data
## Middleware as data
All values in the `:middleware` vector in the route data are coerced into `reitit.ring.middleware/Middleware` Records with using the `reitit.ring.middleware/IntoMiddleware` Protocol. By default, functions, maps and `Middleware` records are allowed.
Records can have arbitrary keys, but the following keys have a special purpose:
| key | description |
| ------------|-------------|
| `:name` | Name of the middleware as a qualified keyword (optional)
| `:wrap` | The actual middleware function of `handler & args => request => response`
| `:gen-wrap` | Middleware function generation function, see [compiling middleware](compiling_middleware.md).
| key | description |
| ---------------|-------------|
| `:name` | Name of the middleware as a qualified keyword (optional)
| `:wrap` | The actual middleware function of `handler & args => request => response`
| `:gen-wrap` | 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.
@ -27,7 +29,7 @@ For the actual request processing, the Records are unwrapped into normal functio
The following produce identical middleware runtime function.
#### Function
### Function
```clj
(defn wrap [handler id]
@ -47,7 +49,7 @@ The following produce identical middleware runtime function.
:wrap wrap}))
```
#### Map
### Map
```clj
(def wrap3
@ -56,7 +58,9 @@ The following produce identical middleware runtime function.
:wrap wrap})
```
### Using Middleware
## Using Middleware
`:middleware` is merged to endpoints by the `router`.
```clj
(require '[reitit.ring :as ring])
@ -72,18 +76,61 @@ The following produce identical middleware runtime function.
:handler handler}}]])))
```
All the middleware are called correctly:
All the middleware are applied correctly:
```clj
(app {:request-method :get, :uri "/api/ping"})
; {:status 200, :body [1 2 3 :handler]}
```
### Future
## Compiling middleware
Middleware can be optimized against an endpoint using [middleware compilation](compiling_middleware.md).
## Transforming the middleware chain
There is an extra option in ring-router (actually, in the undelaying middleware-router): `:reitit.ring.middleware/transform` to transform the middleware chain per endpoint. It sees the vector of compiled middleware and should return a new vector of middleware.
#### Adding debug middleware between all other middleware
```clj
(def app
(ring/ring-handler
(ring/router
["/api" {:middleware [[wrap 1] [wrap2 2]]}
["/ping" {:get {:middleware [[wrap3 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
```clj
(def app
(ring/ring-handler
(ring/router
["/api" {:middleware [[wrap 1] [wrap2 2]]}
["/ping" {:get {:middleware [[wrap3 3]]
:handler handler}}]]
{::middleware/transform reverse)})))
```
```
(app {:request-method :get, :uri "/api/ping"})
; {:status 200, :body [3 2 1 :handler]}
```
## Roadmap for middleware
Some things bubblin' under:
* Hooks to manipulate the `:middleware` chain before compilation
* Re-package all useful middleware into (optimized) data-driven Middleware
* just package or a new community-repo with rehosting stuffm?
* Support `Keyword` expansion into Middleware, enabling external Middleware Registries (duct/integrant/macchiato -style)
* Support Middleware dependency resolution with new keys `:requires` and `:provides`. Values are set of top-level keys of the request. e.g.
* `InjectUserIntoRequestMiddleware` requires `#{:session}` and provides `#{:user}`