mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 00:11:11 +00:00
Implement coercion error handling
This commit is contained in:
parent
4d772c62e1
commit
03d4e8c4bf
2 changed files with 176 additions and 55 deletions
|
|
@ -81,9 +81,11 @@
|
|||
(response-coercion-failed! result coercion value request response)
|
||||
result))))))
|
||||
|
||||
;;
|
||||
;; middleware
|
||||
;;
|
||||
(defn encode-error [data]
|
||||
(-> data
|
||||
(dissoc :request :response)
|
||||
(update :coercion protocol/get-name)
|
||||
(->> (protocol/encode-error (:coercion data)))))
|
||||
|
||||
(defn- coerce-request [coercers request]
|
||||
(reduce-kv
|
||||
|
|
@ -133,6 +135,20 @@
|
|||
(let [coercers (request-coercers coercion parameters)
|
||||
coerced (coerce-parameters coercers request)]
|
||||
(handler (impl/fast-assoc request :parameters coerced) respond raise)))))))
|
||||
(defn handle-coercion-exception [e respond raise]
|
||||
(let [data (ex-data e)]
|
||||
(if-let [status (condp = (:type data)
|
||||
::request-coercion 400
|
||||
::response-coercion 500
|
||||
nil)]
|
||||
(respond
|
||||
{:status status
|
||||
:body (encode-error data)})
|
||||
(raise e))))
|
||||
|
||||
;;
|
||||
;; middleware
|
||||
;;
|
||||
|
||||
(def gen-wrap-coerce-parameters
|
||||
"Middleware for pluggable request coercion.
|
||||
|
|
@ -195,3 +211,24 @@
|
|||
(coerce-response coercers request (handler request)))
|
||||
([request respond raise]
|
||||
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
||||
|
||||
(def gen-wrap-coerce-exceptions
|
||||
"Middleare for coercion exception handling.
|
||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||
and :parameters or :responses from route data, otherwise does not mount."
|
||||
(middleware/create
|
||||
{:name ::coerce-exceptions
|
||||
:gen-wrap (fn [{:keys [coercion parameters responses]} _]
|
||||
(if (and coercion (or parameters responses))
|
||||
(fn [handler]
|
||||
(fn
|
||||
([request]
|
||||
(try
|
||||
(handler request)
|
||||
(catch Exception e
|
||||
(handle-coercion-exception e identity #(throw %)))))
|
||||
([request respond raise]
|
||||
(try
|
||||
(handler request respond (fn [e] (handle-coercion-exception e respond raise)))
|
||||
(catch Throwable e
|
||||
(handle-coercion-exception e respond raise))))))))}))
|
||||
|
|
|
|||
|
|
@ -2,18 +2,43 @@
|
|||
(:require [clojure.test :refer [deftest testing is]]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.ring.coercion :as coercion]
|
||||
[reitit.ring.coercion.spec :as spec])
|
||||
[reitit.ring.coercion.spec :as spec]
|
||||
[schema.core :as s]
|
||||
[reitit.ring.coercion.schema :as schema])
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo))))
|
||||
|
||||
(defn handler
|
||||
([{:keys [::mw]}]
|
||||
{:status 200 :body (conj mw :ok)})
|
||||
([request respond raise]
|
||||
(respond (handler request))))
|
||||
(defn handler [{{{:keys [a]} :query
|
||||
{:keys [b]} :body
|
||||
{:keys [c]} :form
|
||||
{:keys [d]} :header
|
||||
{:keys [e]} :path} :parameters}]
|
||||
{:status 200
|
||||
:body {:total (+ a b c d e)}})
|
||||
|
||||
(deftest coercion-test
|
||||
(let [app (ring/ring-handler
|
||||
(def valid-request
|
||||
{:uri "/api/plus/5"
|
||||
:request-method :get
|
||||
:query-params {"a" "1"}
|
||||
:body-params {:b 2}
|
||||
:form-params {:c 3}
|
||||
:header-params {:d 4}})
|
||||
|
||||
(def invalid-request
|
||||
{:uri "/api/plus/5"
|
||||
:request-method :get})
|
||||
|
||||
(def invalid-request2
|
||||
{:uri "/api/plus/5"
|
||||
:request-method :get
|
||||
:query-params {"a" "1"}
|
||||
:body-params {:b 2}
|
||||
:form-params {:c 3}
|
||||
:header-params {:d -40}})
|
||||
|
||||
(deftest spec-coercion-test
|
||||
(let [create (fn [middleware]
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api"
|
||||
["/plus/:e"
|
||||
|
|
@ -23,41 +48,100 @@
|
|||
:header {:d int?}
|
||||
:path {:e int?}}
|
||||
:responses {200 {:schema {:total pos-int?}}}
|
||||
:handler (fn [{{{:keys [a]} :query
|
||||
{:keys [b]} :body
|
||||
{:keys [c]} :form
|
||||
{:keys [d]} :header
|
||||
{:keys [e]} :path} :parameters}]
|
||||
{:status 200
|
||||
:body {:total (+ a b c d e)}})}}]]
|
||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response]
|
||||
:coercion spec/coercion}}))]
|
||||
:handler handler}}]]
|
||||
{:data {:middleware middleware
|
||||
:coercion spec/coercion}})))]
|
||||
|
||||
(testing "withut exception handling"
|
||||
(let [app (create [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
:body {:total 15}}
|
||||
(app {:uri "/api/plus/5"
|
||||
:request-method :get
|
||||
:query-params {"a" "1"}
|
||||
:body-params {:b 2}
|
||||
:form-params {:c 3}
|
||||
:header-params {:d 4}}))))
|
||||
(app valid-request))))
|
||||
|
||||
(testing "invalid request"
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"Request coercion failed"
|
||||
(app {:uri "/api/plus/5"
|
||||
:request-method :get}))))
|
||||
(app invalid-request))))
|
||||
|
||||
(testing "invalid response"
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"Response coercion failed"
|
||||
(app {:uri "/api/plus/5"
|
||||
:request-method :get
|
||||
:query-params {"a" "1"}
|
||||
:body-params {:b 2}
|
||||
:form-params {:c 3}
|
||||
:header-params {:d -40}}))))))
|
||||
(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])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
:body {:total 15}}
|
||||
(app valid-request))))
|
||||
|
||||
(testing "invalid request"
|
||||
(let [{:keys [status body]} (app invalid-request)]
|
||||
(is (= 400 status))))
|
||||
|
||||
(testing "invalid response"
|
||||
(let [{:keys [status body]} (app invalid-request2)]
|
||||
(is (= 500 status))))))))
|
||||
|
||||
(deftest schema-coercion-test
|
||||
(let [create (fn [middleware]
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api"
|
||||
["/plus/:e"
|
||||
{:get {:parameters {:query {:a s/Int}
|
||||
:body {:b s/Int}
|
||||
:form {:c s/Int}
|
||||
:header {:d s/Int}
|
||||
:path {:e s/Int}}
|
||||
:responses {200 {:schema {:total (s/constrained s/Int pos? 'positive)}}}
|
||||
:handler handler}}]]
|
||||
{:data {:middleware middleware
|
||||
:coercion schema/coercion}})))]
|
||||
|
||||
(testing "withut exception handling"
|
||||
(let [app (create [coercion/gen-wrap-coerce-parameters
|
||||
coercion/gen-wrap-coerce-response])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
:body {:total 15}}
|
||||
(app valid-request))))
|
||||
|
||||
(testing "invalid request"
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"Request coercion failed"
|
||||
(app invalid-request))))
|
||||
|
||||
(testing "invalid response"
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"Response coercion failed"
|
||||
(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])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
:body {:total 15}}
|
||||
(app valid-request))))
|
||||
|
||||
(testing "invalid request"
|
||||
(let [{:keys [status body]} (app invalid-request)]
|
||||
(is (= 400 status))))
|
||||
|
||||
(testing "invalid response"
|
||||
(let [{:keys [status body]} (app invalid-request2)]
|
||||
(is (= 500 status))))))))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue