Update docs

This commit is contained in:
Tommi Reiman 2017-12-09 23:21:03 +02:00
parent dbe40e0145
commit c1ade2f45e
9 changed files with 48 additions and 57 deletions

View file

@ -1,11 +1,11 @@
# Pluggable Coercion # Pluggable Coercion
Reitit provides pluggable parameter coercion via `reitit.ring.coercion.protocol/Coercion` protocol, originally introduced in [compojure-api](https://clojars.org/metosin/compojure-api). Reitit provides pluggable parameter coercion via `reitit.coercion/Coercion` protocol, originally introduced in [compojure-api](https://clojars.org/metosin/compojure-api).
Reitit ships with the following coercion modules: Reitit ships with the following coercion modules:
* `reitit.ring.coercion.schema/SchemaCoercion` for [plumatic schema](https://github.com/plumatic/schema). * `reitit.coercion.schema/coercion` for [plumatic schema](https://github.com/plumatic/schema).
* `reitit.ring.coercion.spec/SpecCoercion` for both [clojure.spec](https://clojure.org/about/spec) and [data-specs](https://github.com/metosin/spec-tools#data-specs). * `reitit.coercion.spec/coercion` for both [clojure.spec](https://clojure.org/about/spec) and [data-specs](https://github.com/metosin/spec-tools#data-specs).
### Ring request and response coercion ### Ring request and response coercion
@ -16,19 +16,19 @@ To use `Coercion` with Ring, one needs to do the following:
* `:responses` map, with response status codes as keys (or `:default` for "everything else") with maps with `:schema` and optionally `:description` as values. * `:responses` map, with response status codes as keys (or `:default` for "everything else") with maps with `:schema` and optionally `:description` as values.
2. Set a `Coercion` implementation to route data under `:coercion` 2. Set a `Coercion` implementation to route data under `:coercion`
3. Mount request & response coercion middleware to the routes (can be done for all routes as the middleware are only mounted to routes which have the parameters &/ responses defined): 3. Mount request & response coercion middleware to the routes (can be done for all routes as the middleware are only mounted to routes which have the parameters &/ responses defined):
* `reitit.ring.coercion/coerce-request-middleware` * `reitit.ring.coercion-middleware/coerce-request-middleware`
* `reitit.ring.coercion/coerce-response-middleware` * `reitit.ring.coercion-middleware/coerce-response-middleware`
If the request coercion succeeds, the coerced parameters are injected into request under `:parameters`. If the request coercion succeeds, the coerced parameters are injected into request under `:parameters`.
If either request or response coercion fails, an descriptive error is thrown. To turn the exceptions into http responses, one can also mount the `reitit.ring.coercion/coerce-exceptions-middleware` middleware If either request or response coercion fails, an descriptive error is thrown. To turn the exceptions into http responses, one can also mount the `reitit.ring.coercion-middleware/coerce-exceptions-middleware` middleware
### Example with Schema ### Example with Schema
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])
(require '[reitit.ring.coercion :as coercion]) (require '[reitit.ring.coercion-middleware :as coercion-middleware])
(require '[reitit.ring.coercion.schema :as schema]) (require '[reitit.coercion.schema :as schema])
(require '[schema.core :as s]) (require '[schema.core :as s])
(def app (def app
@ -36,13 +36,13 @@ If either request or response coercion fails, an descriptive error is thrown. To
(ring/router (ring/router
["/api" ["/api"
["/ping" {:post {:parameters {:body {:x s/Int, :y s/Int}} ["/ping" {:post {:parameters {:body {:x s/Int, :y s/Int}}
:responses {200 {:schema {:total (s/constrained s/Int pos?}}} :responses {200 {:schema {:total (s/constrained s/Int pos?)}}}
:handler (fn [{{{:keys [x y]} :body} :parameters}] :handler (fn [{{{:keys [x y]} :body} :parameters}]
{:status 200 {:status 200
:body {:total (+ x y)}})}}]] :body {:total (+ x y)}})}}]]
{:data {:middleware [coercion/coerce-exceptions-middleware {:data {:middleware [coercion-middleware/coerce-exceptions-middleware
coercion/coerce-request-middleware coercion-middleware/coerce-request-middleware
coercion/coerce-response-middleware] coercion-middleware/coerce-response-middleware]
:coercion schema/coercion}}))) :coercion schema/coercion}})))
``` ```
@ -65,7 +65,7 @@ Invalid request:
:uri "/api/ping" :uri "/api/ping"
:body-params {:x 1, :y "2"}}) :body-params {:x 1, :y "2"}})
; {:status 400, ; {:status 400,
; :body {:type :reitit.ring.coercion/request-coercion ; :body {:type :reitit.coercion/request-coercion
; :coercion :schema ; :coercion :schema
; :in [:request :body-params] ; :in [:request :body-params]
; :value {:x 1, :y "2"} ; :value {:x 1, :y "2"}
@ -77,8 +77,8 @@ Invalid request:
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])
(require '[reitit.ring.coercion :as coercion]) (require '[reitit.ring.coercion-middleware :as coercion-middleware])
(require '[reitit.ring.coercion.spec :as spec]) (require '[reitit.coercion.spec :as spec])
(def app (def app
(ring/ring-handler (ring/ring-handler
@ -89,9 +89,9 @@ Invalid request:
:handler (fn [{{{:keys [x y]} :body} :parameters}] :handler (fn [{{{:keys [x y]} :body} :parameters}]
{:status 200 {:status 200
:body {:total (+ x y)}})}}]] :body {:total (+ x y)}})}}]]
{:data {:middleware [coercion/coerce-exceptions-middleware {:data {:middleware [coercion-middleware/coerce-exceptions-middleware
coercion/coerce-request-middleware coercion-middleware/coerce-request-middleware
coercion/coerce-response-middleware] coercion-middleware/coerce-response-middleware]
:coercion spec/coercion}}))) :coercion spec/coercion}})))
``` ```
@ -132,8 +132,8 @@ Currently, `clojure.spec` [doesn't support runtime transformations via conformin
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])
(require '[reitit.ring.coercion :as coercion]) (require '[reitit.ring.coercion-middleware :as coercion-middleware])
(require '[reitit.ring.coercion.spec :as spec]) (require '[reitit.coercion.spec :as spec])
(require '[clojure.spec.alpha :as s]) (require '[clojure.spec.alpha :as s])
(require '[spec-tools.core :as st]) (require '[spec-tools.core :as st])
@ -152,9 +152,9 @@ Currently, `clojure.spec` [doesn't support runtime transformations via conformin
:handler (fn [{{{:keys [x y]} :body} :parameters}] :handler (fn [{{{:keys [x y]} :body} :parameters}]
{:status 200 {:status 200
:body {:total (+ x y)}})}}]] :body {:total (+ x y)}})}}]]
{:data {:middleware [coercion/coerce-exceptions-middleware {:data {:middleware [coercion-middleware/coerce-exceptions-middleware
coercion/coerce-request-middleware coercion-middleware/coerce-request-middleware
coercion/coerce-response-middleware] coercion-middleware/coerce-response-middleware]
:coercion spec/coercion}}))) :coercion spec/coercion}})))
``` ```
@ -194,16 +194,16 @@ Invalid request:
Both Schema and Spec Coercion can be configured via options, see the source code for details. Both Schema and Spec Coercion can be configured via options, see the source code for details.
To plug in new validation engine, see the To plug in new validation engine, see the
`reitit.ring.coercion.protocol/Coercion` protocol. `reitit.coercion/Coercion` protocol.
```clj ```clj
(defprotocol Coercion (defprotocol Coercion
"Pluggable coercion protocol" "Pluggable coercion protocol"
(get-name [this] "Keyword name for the coercion") (-get-name [this] "Keyword name for the coercion")
(get-apidocs [this model data] "???") (-get-apidocs [this model data] "???")
(compile-model [this model name] "Compiles a coercion model") (-compile-model [this model name] "Compiles a coercion model")
(open-model [this model] "Returns a new map model which doesn't fail on extra keys") (-open-model [this model] "Returns a new map model which doesn't fail on extra keys")
(encode-error [this error] "Converts error in to a serializable format") (-encode-error [this error] "Converts error in to a serializable format")
(request-coercer [this type model] "Returns a `value format => value` request coercion function") (-request-coercer [this type model] "Returns a `value format => value` request coercion function")
(response-coercer [this model] "Returns a `value format => value` response coercion function")) (-response-coercer [this model] "Returns a `value format => value` response coercion function"))
``` ```

View file

@ -1,6 +1,5 @@
(ns example.dspec (ns example.dspec
(:require [reitit.ring.coercion-middleware :as coercion] (:require [reitit.coercion.spec :as spec-coercion]
[reitit.ring.coercion.spec :as spec-coercion]
[example.server :as server])) [example.server :as server]))
(defn handler [{{{:keys [x y]} :query} :parameters}] (defn handler [{{{:keys [x y]} :query} :parameters}]

View file

@ -1,6 +1,5 @@
(ns example.schema (ns example.schema
(:require [reitit.ring.coercion-middleware :as coercion] (:require [reitit.coercion.schema :as schema-coercion]
[reitit.ring.coercion.schema :as schema-coercion]
[example.server :as server])) [example.server :as server]))
(defn handler [{{{:keys [x y]} :query} :parameters}] (defn handler [{{{:keys [x y]} :query} :parameters}]

View file

@ -1,7 +1,7 @@
(ns example.server (ns example.server
(:require [ring.adapter.jetty :as jetty] (:require [ring.adapter.jetty :as jetty]
[reitit.middleware :as middleware] [reitit.middleware :as middleware]
[reitit.ring.coercion-middleware :as coercion])) [reitit.ring.coercion-middleware :as coercion-middleware]))
(defonce ^:private server (atom nil)) (defonce ^:private server (atom nil))
@ -10,9 +10,9 @@
;; to be set with :extract-request-format and extract-response-format ;; to be set with :extract-request-format and extract-response-format
(defn wrap-coercion [handler resource] (defn wrap-coercion [handler resource]
(middleware/chain (middleware/chain
[coercion/coerce-request-middleware [coercion-middleware/coerce-request-middleware
coercion/coerce-response-middleware coercion-middleware/coerce-response-middleware
coercion/coerce-exceptions-middleware] coercion-middleware/coerce-exceptions-middleware]
handler handler
resource)) resource))

View file

@ -1,8 +1,7 @@
(ns example.spec (ns example.spec
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[spec-tools.spec :as spec] [spec-tools.spec :as spec]
[reitit.ring.coercion-middleware :as coercion] [reitit.coercion.spec :as spec-coercion]
[reitit.ring.coercion.spec :as spec-coercion]
[example.server :as server])) [example.server :as server]))
;; wrap into Spec Records to enable runtime conforming ;; wrap into Spec Records to enable runtime conforming

View file

@ -1,11 +1,9 @@
(ns example.dspec (ns example.dspec
(:require [reitit.ring.coercion-middleware :as coercion] (:require [reitit.coercion.spec :as spec-coercion]))
[reitit.ring.coercion.spec :as spec-coercion]))
(def routes (def routes
["/dspec" ["/dspec" {:coercion spec-coercion/coercion}
["/plus" {:name ::plus ["/plus" {:name ::plus
:coercion spec-coercion/coercion
:responses {200 {:schema {:total int?}}} :responses {200 {:schema {:total int?}}}
:get {:summary "plus with query-params" :get {:summary "plus with query-params"
:parameters {:query {:x int?, :y int?}} :parameters {:query {:x int?, :y int?}}

View file

@ -1,12 +1,10 @@
(ns example.schema (ns example.schema
(:require [schema.core :as s] (:require [schema.core :as s]
[reitit.ring.coercion-middleware :as coercion] [reitit.coercion.schema :as schema-coercion]))
[reitit.ring.coercion.schema :as schema-coercion]))
(def routes (def routes
["/schema" ["/schema" {:coercion schema-coercion/coercion}
["/plus" {:name ::plus ["/plus" {:name ::plus
:coercion schema-coercion/coercion
:responses {200 {:schema {:total s/Int}}} :responses {200 {:schema {:total s/Int}}}
:get {:summary "plus with query-params" :get {:summary "plus with query-params"
:parameters {:query {:x s/Int, :y s/Int}} :parameters {:query {:x s/Int, :y s/Int}}

View file

@ -3,7 +3,7 @@
[ring.middleware.params] [ring.middleware.params]
[muuntaja.middleware] [muuntaja.middleware]
[reitit.ring :as ring] [reitit.ring :as ring]
[reitit.ring.coercion-middleware :as coercion] [reitit.ring.coercion-middleware :as coercion-middleware]
[example.dspec] [example.dspec]
[example.schema] [example.schema]
[example.spec])) [example.spec]))
@ -18,9 +18,9 @@
example.spec/routes] example.spec/routes]
{:data {:middleware [ring.middleware.params/wrap-params {:data {:middleware [ring.middleware.params/wrap-params
muuntaja.middleware/wrap-format muuntaja.middleware/wrap-format
coercion/coerce-exceptions-middleware coercion-middleware/coerce-exceptions-middleware
coercion/coerce-request-middleware coercion-middleware/coerce-request-middleware
coercion/coerce-response-middleware]}}))) coercion-middleware/coerce-response-middleware]}})))
(defn restart [] (defn restart []
(swap! server (fn [x] (swap! server (fn [x]

View file

@ -1,8 +1,7 @@
(ns example.spec (ns example.spec
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[spec-tools.spec :as spec] [spec-tools.spec :as spec]
[reitit.ring.coercion-middleware :as coercion] [reitit.coercion.spec :as spec-coercion]))
[reitit.ring.coercion.spec :as spec-coercion]))
;; wrap into Spec Records to enable runtime conforming ;; wrap into Spec Records to enable runtime conforming
(s/def ::x spec/int?) (s/def ::x spec/int?)
@ -10,9 +9,8 @@
(s/def ::total spec/int?) (s/def ::total spec/int?)
(def routes (def routes
["/spec" ["/spec" {:coercion spec-coercion/coercion}
["/plus" {:name ::plus ["/plus" {:name ::plus
:coercion spec-coercion/coercion
:responses {200 {:schema (s/keys :req-un [::total])}} :responses {200 {:schema (s/keys :req-un [::total])}}
:get {:summary "plus with query-params" :get {:summary "plus with query-params"
:parameters {:query (s/keys :req-un [::x ::y])} :parameters {:query (s/keys :req-un [::x ::y])}