From 2ab54a1b9952b0a2ff46584ea0500e0187d7dc7d Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 28 Jul 2018 12:12:53 +0300 Subject: [PATCH] Exception middleware revisited --- .../src/reitit/ring/middleware/exception.clj | 125 ++++++++++++------ .../reitit/ring/middleware/exception_test.clj | 4 +- 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj b/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj index d07b959d..ef4fb1b7 100644 --- a/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj +++ b/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj @@ -1,6 +1,14 @@ (ns reitit.ring.middleware.exception (: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] (loop [sk (.getSuperclass k), ks []] @@ -28,6 +36,20 @@ (catch Exception 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 ;; @@ -60,51 +82,80 @@ ;; public api ;; -(def default-handlers - {::default default-handler - ::ring/response http-response-handler - :muuntaja/decode request-parsing-handler - ::coercion/request-coercion (create-coercion-handler 400) - ::coercion/response-coercion (create-coercion-handler 500)}) +(def default-options + {:handlers {::default default-handler + ::ring/response http-response-handler + :muuntaja/decode request-parsing-handler + ::coercion/request-coercion (create-coercion-handler 400) + ::coercion/response-coercion (create-coercion-handler 500)}}) -(defn wrap-exception [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))))))) +(defn wrap-exception + "Ring middleware that catches all exceptions and looks up a + exceptions handler of type `exception request => response` to + handle the exception. + + The following options are supported: + + | key | description + |--------------|------------- + | `: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" + [handler options] + (-> options wrap handler)) (def exception-middleware - "Middleware that catches all exceptions and looks up a exception handler - from a [[default-handlers]] map in the lookup order: + "Reitit middleware that catches all exceptions and looks up a + exceptions handler of type `exception request => response` to + handle the exception. - 1) `:type` of ex-data - 2) Class of Exception - 3) descadents `:type` of ex-data - 4) Super Classes of Exception + The following options are supported: + + | key | description + |--------------|------------- + | `: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" {:name ::exception - :wrap (wrap-exception default-handlers)}) + :spec ::spec + :wrap (wrap default-options)}) (defn create-exception-middleware - "Creates a middleware that catches all exceptions and looks up a exception handler - from a given map of `handlers` with keyword or Exception class as keys and a 2-arity - Exception handler function as values. + "Creates a reitit middleware that catches all exceptions and looks up a + exceptions handler of type `exception request => response` to + handle the exception. - 1) `:type` of ex-data - 2) Class of Exception - 3) descadents `:type` of ex-data - 4) Super Classes of Exception + The following options are supported: + + | key | description + |--------------|------------- + | `: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" ([] - (create-exception-middleware default-handlers)) - ([handlers] + (create-exception-middleware default-options)) + ([options] {:name ::exception - :wrap (wrap-exception handlers)})) + :spec ::spec + :wrap (wrap options)})) diff --git a/test/clj/reitit/ring/middleware/exception_test.clj b/test/clj/reitit/ring/middleware/exception_test.clj index 4bdb90eb..9f897c78 100644 --- a/test/clj/reitit/ring/middleware/exception_test.clj +++ b/test/clj/reitit/ring/middleware/exception_test.clj @@ -23,8 +23,8 @@ :responses {200 {:body {:total pos-int?}}} :handler f}]] {:data {:middleware [(exception/create-exception-middleware - (merge - exception/default-handlers + (update + exception/default-options :handlers merge {::kikka (constantly {:status 200, :body "kikka"}) SQLException (constantly {:status 200, :body "sql"})}))]}})))]