diff --git a/doc/SUMMARY.md b/doc/SUMMARY.md index 1e303093..0564015e 100644 --- a/doc/SUMMARY.md +++ b/doc/SUMMARY.md @@ -28,6 +28,7 @@ * [Dynamic Extensions](ring/dynamic_extensions.md) * [Data-driven Middleware](ring/data_driven_middleware.md) * [Middleware Registry](ring/middleware_registry.md) + * [Default Middleware](ring/default_middleware.md) * [Pluggable Coercion](ring/coercion.md) * [Route Data Validation](ring/route_data_validation.md) * [Compiling Middleware](ring/compiling_middleware.md) diff --git a/doc/ring/README.md b/doc/ring/README.md index 87356e08..f215c0f7 100644 --- a/doc/ring/README.md +++ b/doc/ring/README.md @@ -7,6 +7,7 @@ * [Dynamic Extensions](dynamic_extensions.md) * [Data-driven Middleware](data_driven_middleware.md) * [Middleware Registry](middleware_registry.md) +* [Default Middleware](default_middleware.md) * [Pluggable Coercion](coercion.md) * [Route Data Validation](route_data_validation.md) * [Compiling Middleware](compiling_middleware.md) diff --git a/doc/ring/extra_middleware.md b/doc/ring/extra_middleware.md new file mode 100644 index 00000000..82b61a8d --- /dev/null +++ b/doc/ring/extra_middleware.md @@ -0,0 +1,65 @@ +# Default Middleware + +Any Ring middleware can be used with `reitit-ring`, using data-driven middleware is preferred as the configuration , and in many cases, 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"] +``` + +Any Ring middlware can be used with `reitit-ring`. + +`ring-handler` injects the `Match` into a request and it can be extracted at runtime with `reitit.ring/get-match`. This can be used to build ad-hoc extensions to the system. + +Example middleware to guard routes based on user roles: + +```clj +(require '[reitit.ring :as ring]) +(require '[clojure.set :as set]) + +(defn wrap-enforce-roles [handler] + (fn [{:keys [::roles] :as request}] + (let [required (some-> request (ring/get-match) :data ::roles)] + (if (and (seq required) (not (set/subset? required roles))) + {:status 403, :body "forbidden"} + (handler request))))) +``` + +Mounted to an app via router data (effecting all routes): + +```clj +(def handler (constantly {:status 200, :body "ok"})) + +(def app + (ring/ring-handler + (ring/router + [["/api" + ["/ping" handler] + ["/admin" {::roles #{:admin}} + ["/ping" handler]]]] + {:data {:middleware [wrap-enforce-roles]}}))) +``` + +Anonymous access to public route: + +```clj +(app {:request-method :get, :uri "/api/ping"}) +; {:status 200, :body "ok"} +``` + +Anonymous access to guarded route: + +```clj +(app {:request-method :get, :uri "/api/admin/ping"}) +; {:status 403, :body "forbidden"} +``` + +Authorized access to guarded route: + +```clj +(app {:request-method :get, :uri "/api/admin/ping", ::roles #{:admin}}) +; {:status 200, :body "ok"} +``` + +Dynamic extensions are nice, but we can do much better. See [data-driven middleware](data_driven_middleware.md) and [compiling routes](compiling_middleware.md).