mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 08:21:11 +00:00
README & small fixes
This commit is contained in:
parent
dcd559bf27
commit
c7c4013f97
6 changed files with 227 additions and 65 deletions
232
README.md
232
README.md
|
|
@ -6,6 +6,8 @@ A friendly data-driven router for Clojure(Script).
|
||||||
* First-class route meta-data
|
* First-class route meta-data
|
||||||
* Generic, not tied to HTTP
|
* Generic, not tied to HTTP
|
||||||
* [Route conflict resolution](#route-conflicts)
|
* [Route conflict resolution](#route-conflicts)
|
||||||
|
* [Pluggable coercion](#parameter-coercion) ([clojure.spec](https://clojure.org/about/spec))
|
||||||
|
* Middleware & Interceptors
|
||||||
* Extendable
|
* Extendable
|
||||||
* Fast
|
* Fast
|
||||||
|
|
||||||
|
|
@ -68,7 +70,7 @@ Same routes flattened:
|
||||||
|
|
||||||
For routing, a `Router` is needed. Reitit ships with several different router implementations: `:linear-router`, `:lookup-router` and `:mixed-router`, based on the awesome [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation.
|
For routing, a `Router` is needed. Reitit ships with several different router implementations: `:linear-router`, `:lookup-router` and `:mixed-router`, based on the awesome [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation.
|
||||||
|
|
||||||
`Router` is created with `reitit.core/router`, which takes routes and optional options map as arguments. The route tree gets expanded, optionally coerced and compiled. The actual `Router` implementation is selected based on the route tree or can be selected with the `:router` option. `Router` support both fast path- and name-based lookups.
|
`Router` is created with `reitit.core/router`, which takes routes and optional options map as arguments. The route tree gets expanded, optionally coerced and compiled. Actual `Router` implementation is selected automatically but can be defined with a `:router` option. `Router` support both path- and name-based lookups.
|
||||||
|
|
||||||
Creating a router:
|
Creating a router:
|
||||||
|
|
||||||
|
|
@ -82,7 +84,7 @@ Creating a router:
|
||||||
["/user/:id" ::user]]]))
|
["/user/:id" ::user]]]))
|
||||||
```
|
```
|
||||||
|
|
||||||
`:mixed-router` is created (both static & wild routes are used):
|
`:mixed-router` is created (both static & wild routes are found):
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(reitit/router-type router)
|
(reitit/router-type router)
|
||||||
|
|
@ -157,52 +159,47 @@ Routes can have arbitrary meta-data. For nested routes, the meta-data is accumul
|
||||||
A router based on nested route tree:
|
A router based on nested route tree:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(def router
|
|
||||||
(reitit/router
|
(reitit/router
|
||||||
["/api" {:interceptors [::api]}
|
["/api" {:interceptors [::api]}
|
||||||
["/ping" ::ping]
|
["/ping" ::ping]
|
||||||
["/public/*path" ::resources]
|
["/admin" {:roles #{:admin}}
|
||||||
["/user/:id" {:name ::get-user
|
["/users" ::users]
|
||||||
:parameters {:id String}}
|
["/db" {:interceptors [::db], :roles ^:replace #{:db-admin}}
|
||||||
["/orders" ::user-orders]]
|
["/:db" {:parameters {:db String}}
|
||||||
["/admin" {:interceptors [::admin]
|
["/drop" ::drop-db]
|
||||||
:roles #{:admin}}
|
["/stats" ::db-stats]]]]]))
|
||||||
["/root" {:name ::root
|
|
||||||
:roles ^:replace #{:root}}]
|
|
||||||
["/db" {:name ::db
|
|
||||||
:interceptors [::db]}]]]))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Resolved route tree:
|
Resolved route tree:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(reitit/routes router)
|
(reitit/routes router)
|
||||||
; [["/api/ping" {:name :user/ping
|
; [["/api/ping" {:interceptors [::api]
|
||||||
; :interceptors [::api]}]
|
; :name ::ping}]
|
||||||
; ["/api/public/*path" {:name :user/resources
|
; ["/api/admin/users" {:interceptors [::api]
|
||||||
; :interceptors [::api]}]
|
; :roles #{:admin}
|
||||||
; ["/api/user/:id/orders" {:name :user/user-orders
|
; :name ::users}]
|
||||||
; :interceptors [::api]
|
; ["/api/admin/db/:db/drop" {:interceptors [::api ::db]
|
||||||
; :parameters {:id String}}]
|
; :roles #{:db-admin}
|
||||||
; ["/api/admin/root" {:name :user/root
|
; :parameters {:db String}
|
||||||
; :interceptors [::api ::admin]
|
; :name ::drop-db}]
|
||||||
; :roles #{:root}}]
|
; ["/api/admin/db/:db/stats" {:interceptors [::api ::db]
|
||||||
; ["/api/admin/db" {:name :user/db
|
; :roles #{:db-admin}
|
||||||
; :interceptors [::api ::admin ::db]
|
; :parameters {:db String}
|
||||||
; :roles #{:admin}}]]
|
; :name ::db-stats}]]
|
||||||
```
|
```
|
||||||
|
|
||||||
Path-based routing:
|
Path-based routing:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(reitit/match-by-path router "/api/admin/root")
|
(reitit/match-by-path router "/api/admin/users")
|
||||||
; #Match{:template "/api/admin/root"
|
; #Match{:template "/api/admin/users"
|
||||||
; :meta {:name :user/root
|
; :meta {:interceptors [::api]
|
||||||
; :interceptors [::api ::admin]
|
; :roles #{:admin}
|
||||||
; :roles #{:root}}
|
; :name ::users}
|
||||||
; :path "/api/admin/root"
|
|
||||||
; :result nil
|
; :result nil
|
||||||
; :params {}}
|
; :params {}
|
||||||
|
; :path "/api/admin/users"}
|
||||||
```
|
```
|
||||||
|
|
||||||
On match, route meta-data is returned and can interpreted by the application.
|
On match, route meta-data is returned and can interpreted by the application.
|
||||||
|
|
@ -333,8 +330,6 @@ Middleware is applied correctly:
|
||||||
; {:status 200, :body [:api :handler]}
|
; {:status 200, :body [:api :handler]}
|
||||||
```
|
```
|
||||||
|
|
||||||
Nested middleware works too:
|
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(app {:request-method :delete, :uri "/api/admin/db"})
|
(app {:request-method :delete, :uri "/api/admin/db"})
|
||||||
; {:status 200, :body [:api :admin :db :delete :handler]}
|
; {:status 200, :body [:api :admin :db :delete :handler]}
|
||||||
|
|
@ -342,7 +337,7 @@ Nested middleware works too:
|
||||||
|
|
||||||
### Async Ring
|
### Async Ring
|
||||||
|
|
||||||
Ring-router supports also 3-arity [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html), so it can be used on [Node.js](https://nodejs.org/en/) too.
|
All built-in middleware provide both the 2 and 3-arity, so they work with [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) too.
|
||||||
|
|
||||||
### Meta-data based extensions
|
### Meta-data based extensions
|
||||||
|
|
||||||
|
|
@ -397,6 +392,169 @@ Authorized access to guarded route:
|
||||||
; {:status 200, :body "ok"}
|
; {:status 200, :body "ok"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Parameter coercion
|
||||||
|
|
||||||
|
Reitit ships with pluggable parameter coercion via `reitit.coercion.protocol/Coercion` protocol. `reitit.coercion.spec/SpecCoercion` provides implements it for [clojure.spec](https://clojure.org/about/spec) & [data-specs](https://github.com/metosin/spec-tools#data-specs).
|
||||||
|
|
||||||
|
**NOTE**: to use the spec-coercion, one needs to add the following dependencies manually to the project:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
[org.clojure/clojure "1.9.0-alpha17"]
|
||||||
|
[org.clojure/spec.alpha "0.1.123"]
|
||||||
|
[metosin/spec-tools "0.3.2"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ring request and response coercion
|
||||||
|
|
||||||
|
To use `Coercion` with Ring, one needs to do the following:
|
||||||
|
|
||||||
|
1. Define parameters and responses as data into route meta-data, in format adopted from [ring-swagger](https://github.com/metosin/ring-swagger#more-complete-example):
|
||||||
|
* `:parameters` map, with submaps for different parameters: `:query`, `:body`, `:form`, `:header` and `:path`. Parameters are defined in the format understood by the `Coercion`.
|
||||||
|
* `:responses` map, with response status codes as keys (or `:default` for "everything else") with maps with `:schema` and optionally `:description` as values.
|
||||||
|
2. Define a `Coercion` to route meta-data under `:coercion`
|
||||||
|
3. Mount request & response coercion middleware to the routes.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Example with data-specs
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[reitit.ring :as ring])
|
||||||
|
(require '[reitit.coercion :as coercion])
|
||||||
|
(require '[reitit.coercion.spec :as spec])
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
["/api"
|
||||||
|
["/ping" {:parameters {:body {:x int?, :y int?}}
|
||||||
|
:responses {200 {:schema {:total pos-int?}}}
|
||||||
|
:get {:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}}]]
|
||||||
|
{:meta {:middleware [coercion/gen-wrap-coerce-parameters
|
||||||
|
coercion/gen-wrap-coerce-response]
|
||||||
|
:coercion spec/coercion}})))
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(app
|
||||||
|
{:request-method :get
|
||||||
|
:uri "/api/ping"
|
||||||
|
:body-params {:x 1, :y 2}})
|
||||||
|
; {:status 200, :body {:total 3}}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example with specs
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[reitit.ring :as ring])
|
||||||
|
(require '[reitit.coercion :as coercion])
|
||||||
|
(require '[reitit.coercion.spec :as spec])
|
||||||
|
(require '[clojure.spec.alpha :as s])
|
||||||
|
(require '[spec-tools.core :as st])
|
||||||
|
|
||||||
|
(s/def ::x (st/spec int?))
|
||||||
|
(s/def ::y (st/spec int?))
|
||||||
|
(s/def ::total int?)
|
||||||
|
(s/def ::request (s/keys :req-un [::x ::y]))
|
||||||
|
(s/def ::response (s/keys :req-un [::total]))
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
["/api"
|
||||||
|
["/ping" {:parameters {:body ::request}
|
||||||
|
:responses {200 {:schema ::response}}
|
||||||
|
:get {:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}}]]
|
||||||
|
{:meta {:middleware [coercion/gen-wrap-coerce-parameters
|
||||||
|
coercion/gen-wrap-coerce-response]
|
||||||
|
:coercion spec/coercion}})))
|
||||||
|
```
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(app
|
||||||
|
{:request-method :get
|
||||||
|
:uri "/api/ping"
|
||||||
|
:body-params {:x 1, :y 2}})
|
||||||
|
; {:status 200, :body {:total 3}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compiling Middleware
|
||||||
|
|
||||||
|
The [meta-data extensions](#meta-data-based-extensions) are a easy way to extend the system. Routes meta-data can be trasnformed into any shape (records, functions etc.) in route compilation, enabling easy access at request-time.
|
||||||
|
|
||||||
|
Still, we can do better. As we know the exact route interceptor/middleware is linked to, we can pass the (compiled) route information into the interceptor/middleware at creation-time. It can extract and transform relevant data just for it and pass it into the actual request-handler via a closure. We can do all the static local computations forehand, yielding much lighter runtime processing.
|
||||||
|
|
||||||
|
For middleware, there is a helper `reitit.middleware/gen` for this. It takes a function of `route-meta router-opts => middleware` and returns a special record extending the internal middleware protocols so it can be mounted as normal middleware. The compiled middleware can also decide no to mount itsef byt returning `nil`. Why mount `wrap-enforce-roles` if there are no roles required for that route?
|
||||||
|
|
||||||
|
To demonstrate the two approaches, below are response coercion middleware written in both ways (found in `reitit.coercion`):
|
||||||
|
|
||||||
|
### Naive
|
||||||
|
|
||||||
|
* Extracts the compiled route information on every request.
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(defn wrap-coerce-response
|
||||||
|
"Pluggable response coercion middleware.
|
||||||
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
|
and :responeses from route meta, otherwise does not mount."
|
||||||
|
[handler]
|
||||||
|
(fn
|
||||||
|
([request]
|
||||||
|
(let [response (handler request)
|
||||||
|
method (:request-method request)
|
||||||
|
match (ring/get-match request)
|
||||||
|
responses (-> match :result method :meta :responses)
|
||||||
|
coercion (-> match :meta :coercion)
|
||||||
|
opts (-> match :meta :opts)]
|
||||||
|
(if (and coercion responses)
|
||||||
|
(let [coercers (response-coercers coercion responses opts)
|
||||||
|
coerced (coerce-response coercers request response)]
|
||||||
|
(coerce-response coercers request (handler request)))
|
||||||
|
(handler request))))
|
||||||
|
([request respond raise]
|
||||||
|
(let [response (handler request)
|
||||||
|
method (:request-method request)
|
||||||
|
match (ring/get-match request)
|
||||||
|
responses (-> match :result method :meta :responses)
|
||||||
|
coercion (-> match :meta :coercion)
|
||||||
|
opts (-> match :meta :opts)]
|
||||||
|
(if (and coercion responses)
|
||||||
|
(let [coercers (response-coercers coercion responses opts)
|
||||||
|
coerced (coerce-response coercers request response)]
|
||||||
|
(handler request #(respond (coerce-response coercers request %))))
|
||||||
|
(handler request respond raise))))))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiled
|
||||||
|
|
||||||
|
* Route information is provided via a closure
|
||||||
|
* Pre-compiled coercers
|
||||||
|
* Mounts only if `:coercion` and `:responses` are defined for the route
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(def gen-wrap-coerce-response
|
||||||
|
"Generator for pluggable response coercion middleware.
|
||||||
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
|
and :responses from route meta, otherwise does not mount."
|
||||||
|
(middleware/gen
|
||||||
|
(fn [{:keys [responses coercion opts]} _]
|
||||||
|
(if (and coercion responses)
|
||||||
|
(let [coercers (response-coercers coercion responses opts)]
|
||||||
|
(fn [handler]
|
||||||
|
(fn
|
||||||
|
([request]
|
||||||
|
(coerce-response coercers request (handler request)))
|
||||||
|
([request respond raise]
|
||||||
|
(handler request #(respond (coerce-response coercers request %)) raise)))))))))
|
||||||
|
```
|
||||||
|
|
||||||
## Merging route-trees
|
## Merging route-trees
|
||||||
|
|
||||||
*TODO*
|
*TODO*
|
||||||
|
|
@ -405,7 +563,7 @@ Authorized access to guarded route:
|
||||||
|
|
||||||
*TODO*
|
*TODO*
|
||||||
|
|
||||||
## Schema, Spec, Swagger & Openapi
|
## Swagger & Openapi
|
||||||
|
|
||||||
*TODO*
|
*TODO*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
(defrecord NoOpCoercion []
|
(defrecord NoOpCoercion []
|
||||||
protocol/Coercion
|
protocol/Coercion
|
||||||
(get-name [_] :no-op)
|
(get-name [_] :no-op)
|
||||||
(compile [_ model] model)
|
(compile [_ model _] model)
|
||||||
(get-apidocs [_ _ {:keys [parameters responses] :as info}])
|
(get-apidocs [_ _ {:keys [parameters responses] :as info}])
|
||||||
(make-open [_ spec] spec)
|
(make-open [_ spec] spec)
|
||||||
(encode-error [_ error] error)
|
(encode-error [_ error] error)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
[reitit.impl :as impl]))
|
[reitit.impl :as impl]))
|
||||||
|
|
||||||
(defn get-apidocs [coercion spec info]
|
#_(defn get-apidocs [coercion spec info]
|
||||||
(protocol/get-apidocs coercion spec info))
|
(protocol/get-apidocs coercion spec info))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
@ -61,15 +61,16 @@
|
||||||
(request-coercion-failed! result coercion value in request)
|
(request-coercion-failed! result coercion value in request)
|
||||||
result))))))
|
result))))))
|
||||||
|
|
||||||
(defn- response-format [request response]
|
#_(defn muuntaja-response-format [request response]
|
||||||
(or (-> response :muuntaja/content-type)
|
(or (-> response :muuntaja/content-type)
|
||||||
(some-> request :muuntaja/response :format)))
|
(some-> request :muuntaja/response :format)))
|
||||||
|
|
||||||
(defn response-coercer [coercion model]
|
(defn response-coercer [coercion model {:keys [extract-response-format]
|
||||||
|
:or {extract-response-format (constantly nil)}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
(let [coercer (protocol/response-coercer coercion model)]
|
(let [coercer (protocol/response-coercer coercion model)]
|
||||||
(fn [request response]
|
(fn [request response]
|
||||||
(let [format (response-format request response)
|
(let [format (extract-response-format request response)
|
||||||
value (:body response)
|
value (:body response)
|
||||||
result (coercer value format)]
|
result (coercer value format)]
|
||||||
(if (protocol/error? result)
|
(if (protocol/error? result)
|
||||||
|
|
@ -98,15 +99,15 @@
|
||||||
[k (request-coercer coercion k v)])
|
[k (request-coercer coercion k v)])
|
||||||
(into {})))
|
(into {})))
|
||||||
|
|
||||||
(defn ^:no-doc response-coercers [coercion responses]
|
(defn ^:no-doc response-coercers [coercion responses opts]
|
||||||
(->> (for [[status {:keys [schema]}] responses :when schema]
|
(->> (for [[status {:keys [schema]}] responses :when schema]
|
||||||
[status (response-coercer coercion schema)])
|
[status (response-coercer coercion schema opts)])
|
||||||
(into {})))
|
(into {})))
|
||||||
|
|
||||||
(defn wrap-coerce-parameters
|
(defn wrap-coerce-parameters
|
||||||
"Pluggable request coercion middleware.
|
"Pluggable request coercion middleware.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
from injected route meta, otherwise does not mount."
|
and :parameters from route meta, otherwise does not mount."
|
||||||
[handler]
|
[handler]
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
|
|
@ -132,10 +133,10 @@
|
||||||
(def gen-wrap-coerce-parameters
|
(def gen-wrap-coerce-parameters
|
||||||
"Generator for pluggable request coercion middleware.
|
"Generator for pluggable request coercion middleware.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
from injected route meta, otherwise does not mount."
|
and :parameters from route meta, otherwise does not mount."
|
||||||
(middleware/gen
|
(middleware/gen
|
||||||
(fn [{:keys [parameters coercion]} _]
|
(fn [{:keys [parameters coercion]} _]
|
||||||
(if coercion
|
(if (and coercion parameters)
|
||||||
(let [coercers (request-coercers coercion parameters)]
|
(let [coercers (request-coercers coercion parameters)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
(fn
|
(fn
|
||||||
|
|
@ -149,7 +150,7 @@
|
||||||
(defn wrap-coerce-response
|
(defn wrap-coerce-response
|
||||||
"Pluggable response coercion middleware.
|
"Pluggable response coercion middleware.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
from injected route meta, otherwise does not mount."
|
and :responses from route meta, otherwise does not mount."
|
||||||
[handler]
|
[handler]
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
|
|
@ -157,9 +158,10 @@
|
||||||
method (:request-method request)
|
method (:request-method request)
|
||||||
match (ring/get-match request)
|
match (ring/get-match request)
|
||||||
responses (-> match :result method :meta :responses)
|
responses (-> match :result method :meta :responses)
|
||||||
coercion (-> match :meta :coercion)]
|
coercion (-> match :meta :coercion)
|
||||||
|
opts (-> match :meta :opts)]
|
||||||
(if coercion
|
(if coercion
|
||||||
(let [coercers (response-coercers coercion responses)
|
(let [coercers (response-coercers coercion responses opts)
|
||||||
coerced (coerce-response coercers request response)]
|
coerced (coerce-response coercers request response)]
|
||||||
(coerce-response coercers request (handler request)))
|
(coerce-response coercers request (handler request)))
|
||||||
(handler request))))
|
(handler request))))
|
||||||
|
|
@ -168,9 +170,10 @@
|
||||||
method (:request-method request)
|
method (:request-method request)
|
||||||
match (ring/get-match request)
|
match (ring/get-match request)
|
||||||
responses (-> match :result method :meta :responses)
|
responses (-> match :result method :meta :responses)
|
||||||
coercion (-> match :meta :coercion)]
|
coercion (-> match :meta :coercion)
|
||||||
|
opts (-> match :meta :opts)]
|
||||||
(if coercion
|
(if coercion
|
||||||
(let [coercers (response-coercers coercion responses)
|
(let [coercers (response-coercers coercion responses opts)
|
||||||
coerced (coerce-response coercers request response)]
|
coerced (coerce-response coercers request response)]
|
||||||
(handler request #(respond (coerce-response coercers request %))))
|
(handler request #(respond (coerce-response coercers request %))))
|
||||||
(handler request respond raise))))))
|
(handler request respond raise))))))
|
||||||
|
|
@ -178,11 +181,11 @@
|
||||||
(def gen-wrap-coerce-response
|
(def gen-wrap-coerce-response
|
||||||
"Generator for pluggable response coercion middleware.
|
"Generator for pluggable response coercion middleware.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
from injected route meta, otherwise does not mount."
|
and :responses from route meta, otherwise does not mount."
|
||||||
(middleware/gen
|
(middleware/gen
|
||||||
(fn [{:keys [responses coercion]} _]
|
(fn [{:keys [responses coercion opts]} _]
|
||||||
(if coercion
|
(if (and coercion responses)
|
||||||
(let [coercers (response-coercers coercion responses)]
|
(let [coercers (response-coercers coercion responses opts)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
(:refer-clojure :exclude [compile]))
|
(:refer-clojure :exclude [compile]))
|
||||||
|
|
||||||
(defprotocol Coercion
|
(defprotocol Coercion
|
||||||
(get-name [this])
|
"Pluggable coercion protocol"
|
||||||
(compile [this model])
|
(get-name [this] "Keyword name for the coercion")
|
||||||
(get-apidocs [this model data])
|
(compile [this model name] "Compiles a coercion model")
|
||||||
(make-open [this model])
|
(get-apidocs [this model data] "???")
|
||||||
(encode-error [this error])
|
(make-open [this model] "Returns a new map model which doesn't fail on extra keys")
|
||||||
(request-coercer [this type model])
|
(encode-error [this error] "Converts error in to a serializable format")
|
||||||
(response-coercer [this model]))
|
(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"))
|
||||||
|
|
||||||
(defrecord CoercionError [])
|
(defrecord CoercionError [])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
protocol/Coercion
|
protocol/Coercion
|
||||||
(get-name [_] name)
|
(get-name [_] name)
|
||||||
|
|
||||||
(compile [_ model]
|
(compile [_ model _]
|
||||||
(memoized-specify model))
|
(memoized-specify model))
|
||||||
|
|
||||||
(get-apidocs [_ _ {:keys [parameters responses] :as info}]
|
(get-apidocs [_ _ {:keys [parameters responses] :as info}]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
(ns reitit.middleware-test
|
(ns reitit.coercion-test
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
[reitit.coercion :as coercion]
|
[reitit.coercion :as coercion]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue