From ad0ef978fbb03a7287afa1441f41282c97253d68 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 2 Aug 2018 16:20:39 +0300 Subject: [PATCH] middleware docs --- doc/ring/default_middleware.md | 131 ++++++++++++++++++ doc/ring/extra_middleware.md | 88 ------------ .../reitit/ring/middleware/exception_test.clj | 2 +- 3 files changed, 132 insertions(+), 89 deletions(-) create mode 100644 doc/ring/default_middleware.md delete mode 100644 doc/ring/extra_middleware.md diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md new file mode 100644 index 00000000..f6470b30 --- /dev/null +++ b/doc/ring/default_middleware.md @@ -0,0 +1,131 @@ +# Default Middleware + +```clj +[metosin/reitit-middleware "0.2.0-SNAPSHOT"] +``` + +Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases, yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. + +* [Exception handling](#exception-handling) +* [Content negotiation](#content-negotiation) +* [Multipart request handling](#multipart-request-handling) + +## Exception handling + +A polished version of [compojure-api](https://github.com/metosin/compojure-api) exception handling. Catches all exceptions and invokes configured exception handler. + +```clj +(require '[reitit.ring.middleware.exception :as exception]) +``` + +### `exception/exception-middleware` + +A preconfigured middleware using `exception/default-handlers`. Catches: + +* Request & response [Coercion](coercion.md) exceptions +* [Muuntaja](https://github.com/metosin/muuntaja) decode exceptions +* Exceptions with `:type` of `:reitit.ring/response`, returning `:response` key from `ex-data`. +* Safely all other exceptions + +```clj +(require '[reitit.ring :as ring]) + +(def app + (ring/ring-handler + (ring/router + ["/fail" (fn [_] (throw (Exception. "fail")))] + {:data {:middleware [exception/exception-middleware]}}))) + +(app {:request-method :get, :uri "/fail"}) +;{:status 500 +; :body {:type "exception" +; :class "java.lang.Exception"}} +``` + +### `exception/create-exception-middleware` + +Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thown/raised exception identifier. Exception idenfier is either a `Keyword` or a Exception Class. + +The following handlers special keys are available: + +| key | description +|--------------|------------- +| `::default` | a default exception handler if nothing else mathced (default `exception/default-handler`). +| `::wrap` | a 3-arity handler to wrap the actual handler `handler exception request => response` (no default). + +The handler is selected from the options map by exception idenfitifier in the following lookup order: + +1) `:type` of exception ex-data +2) Class of exception +3) `:type` ancestors of exception ex-data +4) Super Classes of exception +5) The ::default handler + +```clj +;; type hierarchy +(derive ::error ::exception) +(derive ::failure ::exception) +(derive ::horror ::exception) + +(defn handler [message exception request] + {:status 500 + :body {:message message + :exception (.getClass exception) + :data (ex-data exception) + :uri (:uri request)}}) + +(def exception-middleware + (exception/create-exception-middleware + (merge + exception/default-handlers + {;; ex-data with :type ::error + ::error (partial handler "error") + + ;; ex-data with ::exception or ::failure + ::exception (partial handler "exception") + + ;; SQLException and all it's child classes + java.sql.SQLException (partial handler "sql-exception") + + ;; override the default handler + ::exception/default (partial handler "default") + + ;; print stack-traces for all exceptions + ::exception/wrap (fn [handler e request] + (println "ERROR" (pr-str (:uri request))) + (handler e request))}))) + +(def app + (ring/ring-handler + (ring/router + ["/fail" (fn [_] (throw (ex-info "fail" {:type ::failue})))] + {:data {:middleware [exception-middleware]}}))) + +(app {:request-method :get, :uri "/fail"}) +; ERROR "/fail" +; => {:status 500, +; :body {:message "default" +; :exception clojure.lang.ExceptionInfo +; :data {:type :user/failue} +; :uri "/fail"}} +``` + +## Content Negotiation + +Wrapper for [Muuntaja](https://github.com/metosin/muuntaja) middleware for content-negotiation, request decoding and response encoding. Reads configuration from route data and emit's [swagger](swagger.md) `:produces` and `:consumes` definitions automatically. + +```clj +(require '[reitit.ring.middleware.muuntaja :as muuntaja]) +``` + +## Multipart request handling + +Wrapper for [Ring Multipart Middleware](https://github.com/ring-clojure/ring/blob/master/ring-core/src/ring/middleware/multipart_params.clj). Conditionally mounts to an endpoint only if it has `:multipart` params defined. Emits swagger `:consumes` definitions automatically. + +```clj +(require '[reitit.ring.middleware.multipart :as multipart]) +``` + +## Example app + +See an example app with the default middleware in action: https://github.com/metosin/reitit/blob/master/examples/ring-swagger/src/example/server.clj. diff --git a/doc/ring/extra_middleware.md b/doc/ring/extra_middleware.md deleted file mode 100644 index 16b7afe9..00000000 --- a/doc/ring/extra_middleware.md +++ /dev/null @@ -1,88 +0,0 @@ -# Default Middleware - -Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases, yield better performance. - -To make web development easier, `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. - -```clj -[metosin/reitit-middleware "0.2.0-SNAPSHOT"] -``` - -## Exception handling - -A polished version of [compojure-api](https://github.com/metosin/compojure-api) exception handling. Catches all exceptions and invokes configured exception handler. - -```clj -(require '[reitit.ring.middleware.exception :as exception]) -``` - -### `exception/exception-middleware` - -A preconfigured Middleware using `exception/default-handlers`. Catches: - -* request & response [Coercion](coercion.md) exceptions -* [Muuntaja](https://github.com/metosin/muuntaja) decode exceptions -* Exceptions with `:type` of `:reitit.ring/response`, returning `:response` key from `ex-data`. -* safely all other exceptions - -### `exception/create-exception-middleware` - -Creates a reitit middleware that catches all exceptions. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thown/raised exception identifier. Exception idenfier is either a `Keyword` or a Exception Class. - -The following handlers special handlers are available: - -| key | description -|--------------|------------- -| `::default` | a default exception handler if nothing else mathced (default `exception/default-handler`). -| `::wrap` | a 3-arity handler to wrap the actual handler `handler exception request => response` (no default). - -The handler is selected from the options map by exception idenfitifier in the following lookup order: - -1) `:type` of exception ex-data -2) Class of exception -3) `:type` ancestors of exception ex-data -4) Super Classes of exception -5) The ::default handler - -```clj -(require '[reitit.ring.middleware.exception :as exception]) - -;; type hierarchy -(derive ::error ::exception) -(derive ::failure ::exception) -(derive ::horror ::exception) - -(defn handler [message exception request] - {:status 500 - :body {:message message - :exception (str exception) - :uri (:uri request)}}) - -(exception/create-exception-middleware - (merge - exception/default-handlers - {;; ex-data with :type ::error - ::error (partial handler "error") - - ;; ex-data with ::exception or ::failure - ::exception (partial handler "exception") - - ;; SQLException and all it's child classes - java.sql.SQLException (partial handler "sql-exception") - - ;; override the default handler - ::exception/default (partial handler "default") - - ;; print stack-traces for all exceptions - ::exception/wrap (fn [handler e request] - (.printStackTrace e) - (handler e request))})) -``` - -## Content Negotiation - -Wrapper for [Muuntaja](https://github.com/metosin/muuntaja) middleware for content-negotiation, request decoding and response encoding. Reads configuration from route data and emit's [swagger](swagger.md) `:produces` and `:consumes` definitions automatically. - -## Multipart request handling - -Wrapper for [Ring Multipart Middleware](https://github.com/ring-clojure/ring/blob/master/ring-core/src/ring/middleware/multipart_params.clj). Conditionally mounts to an endpoint only if it has `:multipart` params defined. Emits swagger `:consumes` definitions automatically. diff --git a/test/clj/reitit/ring/middleware/exception_test.clj b/test/clj/reitit/ring/middleware/exception_test.clj index ad8c01d7..7e13f7c4 100644 --- a/test/clj/reitit/ring/middleware/exception_test.clj +++ b/test/clj/reitit/ring/middleware/exception_test.clj @@ -12,7 +12,7 @@ (deftest exception-test (letfn [(create ([f] - (create f nil)) + (create f nil)) ([f wrap] (ring/ring-handler (ring/router