http-router

This commit is contained in:
Tommi Reiman 2018-08-17 14:59:02 +03:00
parent 20fa00beaa
commit 044c0d6163
3 changed files with 127 additions and 91 deletions

View file

@ -335,7 +335,7 @@
(str ":single-static-path-router requires exactly 1 static route: " compiled-routes) (str ":single-static-path-router requires exactly 1 static route: " compiled-routes)
{:routes compiled-routes}))) {:routes compiled-routes})))
(let [[n :as names] (find-names compiled-routes opts) (let [[n :as names] (find-names compiled-routes opts)
[[p data result] :as compiled] compiled-routes [[p data result]] compiled-routes
p #?(:clj (.intern ^String p) :cljs p) p #?(:clj (.intern ^String p) :cljs p)
match (->Match p data result {} p) match (->Match p data result {} p)
routes (uncompile-routes compiled-routes)] routes (uncompile-routes compiled-routes)]

View file

@ -5,37 +5,16 @@
[reitit.core :as r] [reitit.core :as r]
[reitit.impl :as impl])) [reitit.impl :as impl]))
(defrecord Endpoint [data handler path method interceptors]) (defrecord Endpoint [data handler path method interceptors queue])
(defn http-handler (defprotocol Executor
"Creates a ring-handler out of a http-router and (queue
an interceptor runner. [this interceptors]
Optionally takes a ring-handler which is called "takes a sequence of interceptors and compiles them to queue for the executor")
in no route matches." (execute
([router runner] [this request interceptors]
(http-handler router runner nil)) [this request interceptors respond raise]
([router runner default-handler] "executes the interceptor chain"))
(let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil))))]
(with-meta
(fn [request]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
interceptors (-> result method :interceptors)
request (-> request
(impl/fast-assoc :path-params path-params)
(impl/fast-assoc ::r/match match)
(impl/fast-assoc ::r/router router))]
(:response (runner interceptors request)))
(default-handler request)))
{::r/router router}))))
(defn get-router [handler]
(-> handler meta ::r/router))
(defn get-match [request]
(::r/match request))
(defn coerce-handler [[path data] {:keys [expand] :as opts}] (defn coerce-handler [[path data] {:keys [expand] :as opts}]
[path (reduce [path (reduce
@ -44,7 +23,7 @@
(update acc method expand opts) (update acc method expand opts)
acc)) data ring/http-methods)]) acc)) data ring/http-methods)])
(defn compile-result [[path data] opts] (defn compile-result [[path data] {:keys [::queue] :as opts}]
(let [[top childs] (ring/group-keys data) (let [[top childs] (ring/group-keys data)
->handler (fn [handler] ->handler (fn [handler]
(if handler (if handler
@ -54,10 +33,13 @@
(let [data (update data :handler ->handler)] (let [data (update data :handler ->handler)]
(interceptor/compile-result [path data] opts scope))) (interceptor/compile-result [path data] opts scope)))
->endpoint (fn [p d m s] ->endpoint (fn [p d m s]
(-> (compile [p d] opts s) (let [compiled (compile [p d] opts s)
interceptors (:interceptors compiled)]
(-> compiled
(map->Endpoint) (map->Endpoint)
(assoc :path p) (assoc :path p)
(assoc :method m))) (assoc :method m)
(assoc :queue ((or queue identity) interceptors)))))
->methods (fn [any? data] ->methods (fn [any? data]
(reduce (reduce
(fn [acc method] (fn [acc method]
@ -94,3 +76,53 @@
([data opts] ([data opts]
(let [opts (meta-merge {:coerce coerce-handler, :compile compile-result} opts)] (let [opts (meta-merge {:coerce coerce-handler, :compile compile-result} opts)]
(r/router data opts)))) (r/router data opts))))
(defn ring-handler
"Creates a ring-handler out of a http-router,
a default ring-handler and options map, with the following keys:
| key | description |
| ----------------|-------------|
| `:executor` | [[Executor]] for the interceptor chain
| `:interceptors` | Optional sequence of interceptors that are always run before any other interceptors, even for the default handler"
[router default-handler {:keys [executor interceptors]}]
(let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil))))
default-queue (queue executor (interceptor/into-interceptor (concat interceptors [default-handler]) nil (r/options router)))
router-opts (-> (r/options router)
(assoc ::queue (partial queue executor))
(update :interceptors (partial concat interceptors)))
router (router (r/routes router) router-opts)]
(with-meta
(fn
([request]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
interceptors (-> result method :interceptors)
request (-> request
(impl/fast-assoc :path-params path-params)
(impl/fast-assoc ::r/match match)
(impl/fast-assoc ::r/router router))]
(execute executor interceptors request))
(execute executor default-queue request)))
([request respond raise]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
interceptors (-> result method :interceptors)
request (-> request
(impl/fast-assoc :path-params path-params)
(impl/fast-assoc ::r/match match)
(impl/fast-assoc ::r/router router))]
(execute executor interceptors request respond raise))
(execute executor default-queue request respond raise))
nil))
{::r/router router})))
(defn get-router [handler]
(-> handler meta ::r/router))
(defn get-match [request]
(::r/match request))

View file

@ -3,8 +3,7 @@
[reitit.middleware :as middleware] [reitit.middleware :as middleware]
[reitit.core :as r] [reitit.core :as r]
[reitit.impl :as impl] [reitit.impl :as impl]
#?@(:clj [ #?@(:clj [[ring.util.mime-type :as mime-type]
[ring.util.mime-type :as mime-type]
[ring.util.response :as response]]) [ring.util.response :as response]])
[clojure.string :as str])) [clojure.string :as str]))
@ -19,6 +18,61 @@
[top (assoc childs k v)] [top (assoc childs k v)]
[(assoc top k v) childs])) [{} {}] data)) [(assoc top k v) childs])) [{} {}] data))
(defn coerce-handler [[path data] {:keys [expand] :as opts}]
[path (reduce
(fn [acc method]
(if (contains? acc method)
(update acc method expand opts)
acc)) data http-methods)])
(defn compile-result [[path data] opts]
(let [[top childs] (group-keys data)
->endpoint (fn [p d m s]
(-> (middleware/compile-result [p d] opts s)
(map->Endpoint)
(assoc :path p)
(assoc :method m)))
->methods (fn [any? data]
(reduce
(fn [acc method]
(cond-> acc
any? (assoc method (->endpoint path data method nil))))
(map->Methods {})
http-methods))]
(if-not (seq childs)
(->methods true top)
(reduce-kv
(fn [acc method data]
(let [data (meta-merge top data)]
(assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data)
childs))))
;;
;; public api
;;
(defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
support for http-methods and Middleware. See [docs](https://metosin.github.io/reitit/)
for details.
Example:
(router
[\"/api\" {:middleware [wrap-format wrap-oauth2]}
[\"/users\" {:get get-user
:post update-user
:delete {:middleware [wrap-delete]
:handler delete-user}}]])
See router options from [[reitit.core/router]] and [[reitit.middleware/router]]."
([data]
(router data nil))
([data opts]
(let [opts (meta-merge {:coerce coerce-handler, :compile compile-result} opts)]
(r/router data opts))))
(defn routes (defn routes
"Create a ring handler by combining several handlers into one." "Create a ring handler by combining several handlers into one."
[& handlers] [& handlers]
@ -156,7 +210,8 @@
(impl/fast-assoc ::r/match match) (impl/fast-assoc ::r/match match)
(impl/fast-assoc ::r/router router))] (impl/fast-assoc ::r/router router))]
((routes handler default-handler) request respond raise)) ((routes handler default-handler) request respond raise))
(default-handler request respond raise)))) (default-handler request respond raise))
nil))
{::r/router router})))) {::r/router router}))))
(defn get-router [handler] (defn get-router [handler]
@ -164,54 +219,3 @@
(defn get-match [request] (defn get-match [request]
(::r/match request)) (::r/match request))
(defn coerce-handler [[path data] {:keys [expand] :as opts}]
[path (reduce
(fn [acc method]
(if (contains? acc method)
(update acc method expand opts)
acc)) data http-methods)])
(defn compile-result [[path data] opts]
(let [[top childs] (group-keys data)
->endpoint (fn [p d m s]
(-> (middleware/compile-result [p d] opts s)
(map->Endpoint)
(assoc :path p)
(assoc :method m)))
->methods (fn [any? data]
(reduce
(fn [acc method]
(cond-> acc
any? (assoc method (->endpoint path data method nil))))
(map->Methods {})
http-methods))]
(if-not (seq childs)
(->methods true top)
(reduce-kv
(fn [acc method data]
(let [data (meta-merge top data)]
(assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data)
childs))))
(defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
support for http-methods and Middleware. See [docs](https://metosin.github.io/reitit/)
for details.
Example:
(router
[\"/api\" {:middleware [wrap-format wrap-oauth2]}
[\"/users\" {:get get-user
:post update-user
:delete {:middleware [wrap-delete]
:handler delete-user}}]])
See router options from [[reitit.core/router]] and [[reitit.middleware/router]]."
([data]
(router data nil))
([data opts]
(let [opts (meta-merge {:coerce coerce-handler, :compile compile-result} opts)]
(r/router data opts))))