mirror of
https://github.com/metosin/reitit.git
synced 2025-12-21 18:11:12 +00:00
gen-wrap-* => *-middleware
This commit is contained in:
parent
e0dc618c4b
commit
a436b32729
5 changed files with 62 additions and 68 deletions
|
|
@ -16,12 +16,12 @@ 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.
|
||||
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):
|
||||
* `reitit.ring.coercion/gen-wrap-coerce-parameters`
|
||||
* `reitit.ring.coercion/gen-wrap-coerce-response`
|
||||
* `reitit.ring.coercion/coerce-request-middleware`
|
||||
* `reitit.ring.coercion/coerce-response-middleware`
|
||||
|
||||
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/gen-wrap-coerce-exceptions` 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/coerce-exceptions-middleware` middleware
|
||||
|
||||
### Example with Schema
|
||||
|
||||
|
|
@ -40,9 +40,9 @@ If either request or response coercion fails, an descriptive error is thrown. To
|
|||
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||
{:status 200
|
||||
:body {:total (+ x y)}})}}]]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-exceptions
|
||||
coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-exceptions-middleware
|
||||
coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion schema/coercion}})))
|
||||
```
|
||||
|
||||
|
|
@ -89,9 +89,9 @@ Invalid request:
|
|||
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||
{:status 200
|
||||
:body {:total (+ x y)}})}}]]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-exceptions
|
||||
coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-exceptions-middleware
|
||||
coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion spec/coercion}})))
|
||||
```
|
||||
|
||||
|
|
@ -152,9 +152,9 @@ Currently, `clojure.spec` [doesn't support runtime transformations via conformin
|
|||
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||
{:status 200
|
||||
:body {:total (+ x y)}})}}]]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-exceptions
|
||||
coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-exceptions-middleware
|
||||
coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion spec/coercion}})))
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
|||
```clj
|
||||
(require '[reitit.ring.middleware :as middleware])
|
||||
|
||||
(def gen-wrap-coerce-response
|
||||
(def coerce-response-middleware
|
||||
"Middleware for pluggable response coercion.
|
||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||
and :responses from route data, otherwise does not mount."
|
||||
|
|
|
|||
|
|
@ -15,16 +15,14 @@
|
|||
|
||||
(defrecord ParameterCoercion [in style keywordize? open?])
|
||||
|
||||
(def valid-type? #{::request-coercion ::response-coercion})
|
||||
|
||||
(def ring-parameter-coercion
|
||||
(def ^:no-doc ring-parameter-coercion
|
||||
{:query (->ParameterCoercion :query-params :string true true)
|
||||
:body (->ParameterCoercion :body-params :body false false)
|
||||
:form (->ParameterCoercion :form-params :string true true)
|
||||
:header (->ParameterCoercion :header-params :string true true)
|
||||
:path (->ParameterCoercion :path-params :string true true)})
|
||||
|
||||
(defn request-coercion-failed! [result coercion value in request]
|
||||
(defn ^:no-doc request-coercion-failed! [result coercion value in request]
|
||||
(throw
|
||||
(ex-info
|
||||
(str "Request coercion failed: " (pr-str result))
|
||||
|
|
@ -36,7 +34,7 @@
|
|||
:in [:request in]
|
||||
:request request}))))
|
||||
|
||||
(defn response-coercion-failed! [result coercion value request response]
|
||||
(defn ^:no-doc response-coercion-failed! [result coercion value request response]
|
||||
(throw
|
||||
(ex-info
|
||||
(str "Response coercion failed: " (pr-str result))
|
||||
|
|
@ -50,27 +48,23 @@
|
|||
:response response}))))
|
||||
|
||||
;; TODO: support faster key walking, walk/keywordize-keys is quite slow...
|
||||
|
||||
(defn request-coercer [coercion type model]
|
||||
(defn ^:no-doc request-coercer [coercion type model {:keys [extract-request-format]
|
||||
:or {extract-request-format (constantly nil)}}]
|
||||
(if coercion
|
||||
(let [{:keys [keywordize? open? in style]} (ring-parameter-coercion type)
|
||||
transform (comp (if keywordize? walk/keywordize-keys identity) in)
|
||||
model (if open? (protocol/make-open coercion model) model)
|
||||
model (if open? (protocol/open-model coercion model) model)
|
||||
coercer (protocol/request-coercer coercion style model)]
|
||||
(fn [request]
|
||||
(let [value (transform request)
|
||||
format (some-> request :muuntaja/request :format)
|
||||
format (extract-request-format request)
|
||||
result (coercer value format)]
|
||||
(if (protocol/error? result)
|
||||
(request-coercion-failed! result coercion value in request)
|
||||
result))))))
|
||||
|
||||
#_(defn muuntaja-response-format [request response]
|
||||
(or (-> response :muuntaja/content-type)
|
||||
(some-> request :muuntaja/response :format)))
|
||||
|
||||
(defn response-coercer [coercion model {:keys [extract-response-format]
|
||||
:or {extract-response-format (constantly nil)}}]
|
||||
(defn ^:no-doc response-coercer [coercion model {:keys [extract-response-format]
|
||||
:or {extract-response-format (constantly nil)}}]
|
||||
(if coercion
|
||||
(let [coercer (protocol/response-coercer coercion model)]
|
||||
(fn [request response]
|
||||
|
|
@ -81,28 +75,28 @@
|
|||
(response-coercion-failed! result coercion value request response)
|
||||
result))))))
|
||||
|
||||
(defn encode-error [data]
|
||||
(defn ^:no-doc encode-error [data]
|
||||
(-> data
|
||||
(dissoc :request :response)
|
||||
(update :coercion protocol/get-name)
|
||||
(->> (protocol/encode-error (:coercion data)))))
|
||||
|
||||
(defn- coerce-request [coercers request]
|
||||
(defn ^:no-doc coerce-request [coercers request]
|
||||
(reduce-kv
|
||||
(fn [acc k coercer]
|
||||
(impl/fast-assoc acc k (coercer request)))
|
||||
{}
|
||||
coercers))
|
||||
|
||||
(defn- coerce-response [coercers request response]
|
||||
(defn ^:no-doc coerce-response [coercers request response]
|
||||
(if response
|
||||
(if-let [coercer (or (coercers (:status response)) (coercers :default))]
|
||||
(impl/fast-assoc response :body (coercer request response)))))
|
||||
|
||||
(defn ^:no-doc request-coercers [coercion parameters]
|
||||
(defn ^:no-doc request-coercers [coercion parameters opts]
|
||||
(->> (for [[k v] parameters
|
||||
:when v]
|
||||
[k (request-coercer coercion k v)])
|
||||
[k (request-coercer coercion k v opts)])
|
||||
(into {})))
|
||||
|
||||
(defn ^:no-doc response-coercers [coercion responses opts]
|
||||
|
|
@ -110,7 +104,7 @@
|
|||
[status (response-coercer coercion schema opts)])
|
||||
(into {})))
|
||||
|
||||
(defn handle-coercion-exception [e respond raise]
|
||||
(defn ^:no-doc handle-coercion-exception [e respond raise]
|
||||
(let [data (ex-data e)]
|
||||
(if-let [status (condp = (:type data)
|
||||
::request-coercion 400
|
||||
|
|
@ -125,15 +119,15 @@
|
|||
;; middleware
|
||||
;;
|
||||
|
||||
(def gen-wrap-coerce-parameters
|
||||
(def coerce-request-middleware
|
||||
"Middleware for pluggable request coercion.
|
||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||
and :parameters from route data, otherwise does not mount."
|
||||
(middleware/create
|
||||
{:name ::coerce-parameters
|
||||
:gen-wrap (fn [{:keys [coercion parameters]} _]
|
||||
:gen-wrap (fn [{:keys [coercion parameters]} opts]
|
||||
(if (and coercion parameters)
|
||||
(let [coercers (request-coercers coercion parameters)]
|
||||
(let [coercers (request-coercers coercion parameters opts)]
|
||||
(fn [handler]
|
||||
(fn
|
||||
([request]
|
||||
|
|
@ -143,13 +137,13 @@
|
|||
(let [coerced (coerce-request coercers request)]
|
||||
(handler (impl/fast-assoc request :parameters coerced) respond raise))))))))}))
|
||||
|
||||
(def gen-wrap-coerce-response
|
||||
(def coerce-response-middleware
|
||||
"Middleware for pluggable response coercion.
|
||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||
and :responses from route data, otherwise does not mount."
|
||||
(middleware/create
|
||||
{:name ::coerce-response
|
||||
:gen-wrap (fn [{:keys [coercion responses opts]} _]
|
||||
:gen-wrap (fn [{:keys [coercion responses]} opts]
|
||||
(if (and coercion responses)
|
||||
(let [coercers (response-coercers coercion responses opts)]
|
||||
(fn [handler]
|
||||
|
|
@ -159,7 +153,7 @@
|
|||
([request respond raise]
|
||||
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
||||
|
||||
(def gen-wrap-coerce-exceptions
|
||||
(def coerce-exceptions-middleware
|
||||
"Middleware for handling coercion exceptions.
|
||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||
and :parameters or :responses from route data, otherwise does not mount."
|
||||
|
|
|
|||
|
|
@ -107,24 +107,24 @@
|
|||
app (ring/ring-handler
|
||||
(ring/router
|
||||
routes
|
||||
{:data {:middleware [coercion/wrap-coerce-parameters]
|
||||
{:data {:middleware [coercion/coerce-request-middleware]
|
||||
:coercion coercion}}))
|
||||
app2 (ring/ring-handler
|
||||
(ring/router
|
||||
routes
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters]
|
||||
{:data {:middleware [coercion/coerce-request-middleware]
|
||||
:coercion coercion}}))
|
||||
app3 (ring/ring-handler
|
||||
(ring/router
|
||||
routes
|
||||
{:data {:middleware [coercion/wrap-coerce-parameters
|
||||
{:data {:middleware [coercion/coerce-request-middleware
|
||||
coercion/wrap-coerce-response]
|
||||
:coercion coercion}}))
|
||||
app4 (ring/ring-handler
|
||||
(ring/router
|
||||
routes
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion coercion}}))
|
||||
req {:request-method :get
|
||||
:uri "/api/ping"
|
||||
|
|
@ -138,17 +138,17 @@
|
|||
;; 175ns (-19%)
|
||||
;; 360ns (-64%)
|
||||
;; 4080ns (-30%)
|
||||
(bench! "gen-wrap-coerce-parameters" (app2 req))
|
||||
(bench! "coerce-request-middleware" (app2 req))
|
||||
|
||||
;; 300ns
|
||||
;; 1740ns
|
||||
;; 9400ns
|
||||
(bench! "wrap-coerce-parameters & responses" (app3 req))
|
||||
(bench! "wrap-coerce-request & responses" (app3 req))
|
||||
|
||||
;; 175ns (-42%)
|
||||
;; 384ns (-78%)
|
||||
;; 6100ns (-35%)
|
||||
(bench! "gen-wrap-coerce-parameters & responses" (app4 req)))))
|
||||
(bench! "coerce-request-middleware & responses" (app4 req)))))
|
||||
|
||||
(comment
|
||||
(do
|
||||
|
|
@ -161,8 +161,8 @@
|
|||
:get {:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||
{:status 200
|
||||
:body {:total (+ x y)}})}}]]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion spec/coercion}})))
|
||||
|
||||
(app
|
||||
|
|
@ -205,8 +205,8 @@
|
|||
(let [body (-> request :parameters :body)]
|
||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||
{:data {:middleware [[mm/wrap-format m]
|
||||
coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion schema/coercion}}))
|
||||
request {:request-method :post
|
||||
:uri "/plus"
|
||||
|
|
@ -228,8 +228,8 @@
|
|||
:handler (fn [request]
|
||||
(let [body (-> request :parameters :body)]
|
||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion schema/coercion}}))
|
||||
request {:request-method :post
|
||||
:uri "/plus"
|
||||
|
|
@ -251,8 +251,8 @@
|
|||
:handler (fn [request]
|
||||
(let [body (-> request :parameters :body)]
|
||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion spec/coercion}}))
|
||||
request {:request-method :post
|
||||
:uri "/plus"
|
||||
|
|
@ -279,8 +279,8 @@
|
|||
:handler (fn [request]
|
||||
(let [body (-> request :parameters :body)]
|
||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
{:data {:middleware [coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware]
|
||||
:coercion spec/coercion}}))
|
||||
request {:request-method :post
|
||||
:uri "/plus"
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@
|
|||
:coercion spec/coercion}})))]
|
||||
|
||||
(testing "withut exception handling"
|
||||
(let [app (create [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response])]
|
||||
(let [app (create [coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
|
|
@ -74,9 +74,9 @@
|
|||
(app invalid-request2))))))
|
||||
|
||||
(testing "with exception handling"
|
||||
(let [app (create [coercion/gen-wrap-coerce-exceptions
|
||||
coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response])]
|
||||
(let [app (create [coercion/coerce-exceptions-middleware
|
||||
coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
|
|
@ -108,8 +108,8 @@
|
|||
:coercion schema/coercion}})))]
|
||||
|
||||
(testing "withut exception handling"
|
||||
(let [app (create [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response])]
|
||||
(let [app (create [coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
|
|
@ -129,9 +129,9 @@
|
|||
(app invalid-request2))))
|
||||
|
||||
(testing "with exception handling"
|
||||
(let [app (create [coercion/gen-wrap-coerce-exceptions
|
||||
coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response])]
|
||||
(let [app (create [coercion/coerce-exceptions-middleware
|
||||
coercion/coerce-request-middleware
|
||||
coercion/coerce-response-middleware])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
|
|
|
|||
Loading…
Reference in a new issue