mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
Merge pull request #741 from metosin/readme-example
doc: improve middleware docs & examples in README.md & elsewhere
This commit is contained in:
commit
55c30af979
4 changed files with 79 additions and 27 deletions
39
README.md
39
README.md
|
|
@ -109,6 +109,7 @@ A Ring routing app with input & output coercion using [data-specs](https://githu
|
||||||
(require '[reitit.ring :as ring])
|
(require '[reitit.ring :as ring])
|
||||||
(require '[reitit.coercion.spec])
|
(require '[reitit.coercion.spec])
|
||||||
(require '[reitit.ring.coercion :as rrc])
|
(require '[reitit.ring.coercion :as rrc])
|
||||||
|
(require '[reitit.ring.middleware.exception :as exception])
|
||||||
(require '[reitit.ring.middleware.muuntaja :as muuntaja])
|
(require '[reitit.ring.middleware.muuntaja :as muuntaja])
|
||||||
(require '[reitit.ring.middleware.parameters :as parameters])
|
(require '[reitit.ring.middleware.parameters :as parameters])
|
||||||
|
|
||||||
|
|
@ -124,39 +125,45 @@ A Ring routing app with input & output coercion using [data-specs](https://githu
|
||||||
;; router data affecting all routes
|
;; router data affecting all routes
|
||||||
{:data {:coercion reitit.coercion.spec/coercion
|
{:data {:coercion reitit.coercion.spec/coercion
|
||||||
:muuntaja m/instance
|
:muuntaja m/instance
|
||||||
:middleware [parameters/parameters-middleware
|
:middleware [parameters/parameters-middleware ; decoding query & form params
|
||||||
|
muuntaja/format-middleware ; content negotiation
|
||||||
|
exception/exception-middleware ; converting exceptions to HTTP responses
|
||||||
rrc/coerce-request-middleware
|
rrc/coerce-request-middleware
|
||||||
muuntaja/format-response-middleware
|
|
||||||
rrc/coerce-response-middleware]}})))
|
rrc/coerce-response-middleware]}})))
|
||||||
```
|
```
|
||||||
|
|
||||||
Valid request:
|
Valid request:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(app {:request-method :get
|
(-> (app {:request-method :get
|
||||||
:uri "/api/math"
|
:uri "/api/math"
|
||||||
:query-params {:x "1", :y "2"}})
|
:query-params {:x "1", :y "2"}})
|
||||||
|
(update :body slurp))
|
||||||
; {:status 200
|
; {:status 200
|
||||||
; :body {:total 3}}
|
; :body "{\"total\":3}"
|
||||||
|
; :headers {"Content-Type" "application/json; charset=utf-8"}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Invalid request:
|
Invalid request:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(app {:request-method :get
|
(-> (app {:request-method :get
|
||||||
:uri "/api/math"
|
:uri "/api/math"
|
||||||
:query-params {:x "1", :y "a"}})
|
:query-params {:x "1", :y "a"}})
|
||||||
;{:status 400,
|
(update :body jsonista.core/read-value))
|
||||||
; :body {:type :reitit.coercion/request-coercion,
|
; {:status 400
|
||||||
; :coercion :spec,
|
; :headers {"Content-Type" "application/json; charset=utf-8"}
|
||||||
; :spec "(spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:$spec20745/x :$spec20745/y]), :type :map, :keys #{:y :x}, :keys/req #{:y :x}})",
|
; :body {"spec" "(spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:spec$8974/x :spec$8974/y]), :type :map, :leaf? false})"
|
||||||
; :problems [{:path [:y],
|
; "value" {"x" "1"
|
||||||
; :pred "clojure.core/int?",
|
; "y" "a"}
|
||||||
; :val "a",
|
; "problems" [{"via" ["spec$8974/y"]
|
||||||
; :via [:$spec20745/y],
|
; "path" ["y"]
|
||||||
; :in [:y]}],
|
; "pred" "clojure.core/int?"
|
||||||
; :value {:x "1", :y "a"},
|
; "in" ["y"]
|
||||||
; :in [:request :query-params]}}
|
; "val" "a"}]
|
||||||
|
; "type" "reitit.coercion/request-coercion"
|
||||||
|
; "coercion" "spec"
|
||||||
|
; "in" ["request" "query-params"]}}
|
||||||
```
|
```
|
||||||
|
|
||||||
## More examples
|
## More examples
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It is relatively easy to understand and allows for good performance. A downside is that the middleware chain is just a opaque function, making things like debugging and composition hard. It is too easy to apply the middlewares in wrong order.
|
Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It is relatively easy to understand and allows for good performance. A downside is that the middleware chain is just a opaque function, making things like debugging and composition hard. It is too easy to apply the middlewares in wrong order.
|
||||||
|
|
||||||
|
For the basics of reitit middleware, [read this first](ring.md#middleware).
|
||||||
|
|
||||||
Reitit defines middleware as data:
|
Reitit defines middleware as data:
|
||||||
|
|
||||||
1. A middleware can be defined as first-class data entries
|
1. A middleware can be defined as first-class data entries
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ Any Ring middleware can be used with `reitit-ring`, but using data-driven middle
|
||||||
`reitit.ring.middleware.parameters/parameters-middleware` to capture query- and form-params. Wraps
|
`reitit.ring.middleware.parameters/parameters-middleware` to capture query- and form-params. Wraps
|
||||||
`ring.middleware.params/wrap-params`.
|
`ring.middleware.params/wrap-params`.
|
||||||
|
|
||||||
**NOTE**: This middleware 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. cf. https://github.com/metosin/reitit/issues/134
|
|
||||||
|
|
||||||
## Exception Handling
|
## Exception Handling
|
||||||
|
|
||||||
See [Exception Handling with Ring](exceptions.md).
|
See [Exception Handling with Ring](exceptions.md).
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ Name-based reverse routing:
|
||||||
|
|
||||||
# Middleware
|
# Middleware
|
||||||
|
|
||||||
Middleware can be mounted using a `:middleware` key - either to top-level or under request method submap. Its value should be a vector of `reitit.middleware/IntoMiddleware` values. These include:
|
Middleware can be mounted using a `:middleware` key in [Route Data](../basics/route_data.md) - either to top-level or under request method submap. Its value should be a vector of `reitit.middleware/IntoMiddleware` values. These include:
|
||||||
|
|
||||||
1. normal ring middleware function `handler -> request -> response`
|
1. normal ring middleware function `handler -> request -> response`
|
||||||
2. vector of middleware function `[handler args*] -> request -> response` and it's arguments
|
2. vector of middleware function `[handler args*] -> request -> response` and it's arguments
|
||||||
|
|
@ -194,11 +194,56 @@ Top-level middleware, applied before any routing is done:
|
||||||
(def app
|
(def app
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api" {:middleware [[mw :api]]}
|
["/api" {:middleware [[wrap :api]]}
|
||||||
["/get" {:get handler}]])
|
["/get" {:get handler}]])
|
||||||
nil
|
nil
|
||||||
{:middleware [[mw :top]]}))
|
{:middleware [[wrap :top]]}))
|
||||||
|
|
||||||
(app {:request-method :get, :uri "/api/get"})
|
(app {:request-method :get, :uri "/api/get"})
|
||||||
; {:status 200, :body [:top :api :ok]}
|
; {:status 200, :body [:top :api :ok]}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Same middleware for all routes, using [top-level route data](route_data.md#top-level-route-data):
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(def app
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
["/api"
|
||||||
|
["/get" {:get handler
|
||||||
|
:middleware [[wrap :specific]]}]]
|
||||||
|
{:data {:middleware [[wrap :generic]]}})))
|
||||||
|
|
||||||
|
(app {:request-method :get, :uri "/api/get"})
|
||||||
|
; {:status 200, :body [:generic :specific :handler]}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Execution order
|
||||||
|
|
||||||
|
Here's a full example that shows the execution order of the middleware
|
||||||
|
using all of the above techniques:
|
||||||
|
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(def app
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
["/api" {:middleware [[wrap :3-parent]]}
|
||||||
|
["/get" {:get handler
|
||||||
|
:middleware [[wrap :4-route]]}]]
|
||||||
|
{:data {:middleware [[wrap :2-top-level-route-data]]}})
|
||||||
|
nil
|
||||||
|
{:middleware [[wrap :1-top]]}))
|
||||||
|
|
||||||
|
(app {:request-method :get, :uri "/api/get"})
|
||||||
|
; {:status 200, :body [:1-top :2-top-level-route-data :3-parent :4-route :handler]}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Which method should I use for defining middleware?
|
||||||
|
|
||||||
|
- If you have middleware that you want to apply to the default handler (second argument of `ring/ring-handler`), use _top-level middleware_
|
||||||
|
- If you have a generic middleware, that doesn't depend on the route, use _top-level middleware_ or _top-level route data_
|
||||||
|
- If you are using top-level route data anyway for some other reasons, it might be clearest to have all the middleware there. This is what most of the reitit examples do.
|
||||||
|
- If you want to apply a middleware to only a couple of routes, use _nested middleware_ (ie. _route data_)
|
||||||
|
- If you want a middleware to apply to all routes, but use route-specific data, you need _top-level route data_ combined with [Compiling Middleware](compiling_middleware.md)
|
||||||
|
- This is what many reitit features like [Ring Coercion](coercion.md) do. Check the examples & docs for the reitit features you want to use!
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue