Exception middleware revisited

This commit is contained in:
Tommi Reiman 2018-07-28 12:12:53 +03:00
parent 62cdfa2c52
commit 2ab54a1b99
2 changed files with 90 additions and 39 deletions

View file

@ -1,6 +1,14 @@
(ns reitit.ring.middleware.exception (ns reitit.ring.middleware.exception
(:require [reitit.coercion :as coercion] (:require [reitit.coercion :as coercion]
[reitit.ring :as ring])) [reitit.ring :as ring]
[clojure.spec.alpha :as s]))
(s/def ::handlers (s/map-of any? fn?))
(s/def ::spec (s/keys :opt-un [::handlers]))
;;
;; helpers
;;
(defn- super-classes [^Class k] (defn- super-classes [^Class k]
(loop [sk (.getSuperclass k), ks []] (loop [sk (.getSuperclass k), ks []]
@ -28,6 +36,20 @@
(catch Exception e (catch Exception e
(raise e)))) (raise e))))
(defn- wrap [{:keys [handlers]}]
(fn [handler]
(fn
([request]
(try
(handler request)
(catch Throwable e
(on-exception handlers e request identity #(throw %)))))
([request respond raise]
(try
(handler request respond (fn [e] (on-exception handlers e request respond raise)))
(catch Throwable e
(on-exception handlers e request respond raise)))))))
;; ;;
;; handlers ;; handlers
;; ;;
@ -60,51 +82,80 @@
;; public api ;; public api
;; ;;
(def default-handlers (def default-options
{::default default-handler {:handlers {::default default-handler
::ring/response http-response-handler ::ring/response http-response-handler
:muuntaja/decode request-parsing-handler :muuntaja/decode request-parsing-handler
::coercion/request-coercion (create-coercion-handler 400) ::coercion/request-coercion (create-coercion-handler 400)
::coercion/response-coercion (create-coercion-handler 500)}) ::coercion/response-coercion (create-coercion-handler 500)}})
(defn wrap-exception [handlers] (defn wrap-exception
(fn [handler] "Ring middleware that catches all exceptions and looks up a
(fn exceptions handler of type `exception request => response` to
([request] handle the exception.
(try
(handler request) The following options are supported:
(catch Throwable e
(on-exception handlers e request identity #(throw %))))) | key | description
([request respond raise] |--------------|-------------
(try | `:handlers` | A map of exception identifier => exception-handler
(handler request respond (fn [e] (on-exception handlers e request respond raise)))
(catch Throwable e The handler is selected from the handlers by exception idenfiter
(on-exception handlers e request respond raise))))))) in the following lookup order:
1) `:type` of exception ex-data
2) Class of exception
3) descadents `:type` of exception ex-data
4) Super Classes of exception
5) The ::default handler"
[handler options]
(-> options wrap handler))
(def exception-middleware (def exception-middleware
"Middleware that catches all exceptions and looks up a exception handler "Reitit middleware that catches all exceptions and looks up a
from a [[default-handlers]] map in the lookup order: exceptions handler of type `exception request => response` to
handle the exception.
1) `:type` of ex-data The following options are supported:
2) Class of Exception
3) descadents `:type` of ex-data | key | description
4) Super Classes of Exception |--------------|-------------
| `:handlers` | A map of exception identifier => exception-handler
The handler is selected from the handlers by exception idenfiter
in the following lookup order:
1) `:type` of exception ex-data
2) Class of exception
3) descadents `:type` of exception ex-data
4) Super Classes of exception
5) The ::default handler" 5) The ::default handler"
{:name ::exception {:name ::exception
:wrap (wrap-exception default-handlers)}) :spec ::spec
:wrap (wrap default-options)})
(defn create-exception-middleware (defn create-exception-middleware
"Creates a middleware that catches all exceptions and looks up a exception handler "Creates a reitit middleware that catches all exceptions and looks up a
from a given map of `handlers` with keyword or Exception class as keys and a 2-arity exceptions handler of type `exception request => response` to
Exception handler function as values. handle the exception.
1) `:type` of ex-data The following options are supported:
2) Class of Exception
3) descadents `:type` of ex-data | key | description
4) Super Classes of Exception |--------------|-------------
| `:handlers` | A map of exception identifier => exception-handler
The handler is selected from the handlers by exception idenfiter
in the following lookup order:
1) `:type` of exception ex-data
2) Class of exception
3) descadents `:type` of exception ex-data
4) Super Classes of exception
5) The ::default handler" 5) The ::default handler"
([] ([]
(create-exception-middleware default-handlers)) (create-exception-middleware default-options))
([handlers] ([options]
{:name ::exception {:name ::exception
:wrap (wrap-exception handlers)})) :spec ::spec
:wrap (wrap options)}))

View file

@ -23,8 +23,8 @@
:responses {200 {:body {:total pos-int?}}} :responses {200 {:body {:total pos-int?}}}
:handler f}]] :handler f}]]
{:data {:middleware [(exception/create-exception-middleware {:data {:middleware [(exception/create-exception-middleware
(merge (update
exception/default-handlers exception/default-options :handlers merge
{::kikka (constantly {:status 200, :body "kikka"}) {::kikka (constantly {:status 200, :body "kikka"})
SQLException (constantly {:status 200, :body "sql"})}))]}})))] SQLException (constantly {:status 200, :body "sql"})}))]}})))]