mirror of
https://github.com/metosin/reitit.git
synced 2025-12-22 18:41:10 +00:00
initial http-handler
This commit is contained in:
parent
8603224546
commit
59cbb25688
5 changed files with 213 additions and 6 deletions
|
|
@ -112,8 +112,7 @@
|
|||
(defn compile-result
|
||||
([route opts]
|
||||
(compile-result route opts nil))
|
||||
([[path {:keys [interceptors handler] :as data}] opts scope]
|
||||
(ensure-handler! path data scope)
|
||||
([[_ {:keys [interceptors handler] :as data}] opts _]
|
||||
(map->Endpoint
|
||||
{:interceptors (chain interceptors handler data opts)
|
||||
:data data})))
|
||||
|
|
@ -131,8 +130,8 @@
|
|||
|
||||
Options:
|
||||
|
||||
| key | description |
|
||||
| --------------------------------|-------------|
|
||||
| key | description
|
||||
| --------------------------------|-------------
|
||||
| `:reitit.interceptor/transform` | Function of [Interceptor] => [Interceptor] to transform the expanded Interceptors (default: identity).
|
||||
| `:reitit.interceptor/registry` | Map of `keyword => IntoInterceptor` to replace keyword references into Interceptor
|
||||
|
||||
|
|
|
|||
10
modules/reitit-http/project.clj
Normal file
10
modules/reitit-http/project.clj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
(defproject metosin/reitit-http "0.2.0-SNAPSHOT"
|
||||
:description "Reitit: HTTP routing with interceptors"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:plugins [[lein-parent "0.3.2"]]
|
||||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:dependencies [[metosin/reitit-core]
|
||||
[metosin/reitit-ring]])
|
||||
198
modules/reitit-http/src/reitit/http.cljc
Normal file
198
modules/reitit-http/src/reitit/http.cljc
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
(ns reitit.http
|
||||
(:require [meta-merge.core :refer [meta-merge]]
|
||||
[reitit.interceptor :as interceptor]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.core :as r]
|
||||
[reitit.impl :as impl]))
|
||||
|
||||
(defrecord Endpoint [data handler path method interceptors])
|
||||
|
||||
(defn http-handler
|
||||
"Creates a ring-handler out of a http-router and
|
||||
an interceptor runner.
|
||||
Optionally takes a ring-handler which is called
|
||||
in no route matches."
|
||||
([router runner]
|
||||
(http-handler router runner nil))
|
||||
([router runner default-handler]
|
||||
(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}]
|
||||
[path (reduce
|
||||
(fn [acc method]
|
||||
(if (contains? acc method)
|
||||
(update acc method expand opts)
|
||||
acc)) data ring/http-methods)])
|
||||
|
||||
(defn compile-result [[path data] opts]
|
||||
(let [[top childs] (ring/group-keys data)
|
||||
->handler (fn [handler]
|
||||
(if handler
|
||||
(fn [ctx]
|
||||
(->> ctx :request handler (assoc ctx :response)))))
|
||||
compile (fn [[path data] opts scope]
|
||||
(let [data (update data :handler ->handler)]
|
||||
(interceptor/compile-result [path data] opts scope)))
|
||||
->endpoint (fn [p d m s]
|
||||
(-> (compile [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))))
|
||||
(ring/map->Methods {})
|
||||
ring/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 Interceptors. See [docs](https://metosin.github.io/reitit/)
|
||||
for details.
|
||||
|
||||
Example:
|
||||
|
||||
(router
|
||||
[\"/api\" {:interceptors [format-i oauth2-i]}
|
||||
[\"/users\" {:get get-user
|
||||
:post update-user
|
||||
:delete {:interceptors [delete-i]
|
||||
: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))))
|
||||
|
||||
(ns reitit.interceptor.simple)
|
||||
|
||||
(defn enqueue [ctx interceptors]
|
||||
(update ctx :queue (fnil into clojure.lang.PersistentQueue/EMPTY) interceptors))
|
||||
|
||||
|
||||
(defn leave [ctx stack key]
|
||||
(let [it (clojure.lang.RT/iter stack)]
|
||||
(loop [ctx ctx, key key]
|
||||
(if (.hasNext it)
|
||||
(if-let [f (-> it .next key)]
|
||||
(let [[ctx key] (try
|
||||
[(f ctx)])])
|
||||
(recur
|
||||
(try
|
||||
(leave ctx)))
|
||||
(recur ctx))
|
||||
ctx))))
|
||||
|
||||
(defn try-f [ctx f]
|
||||
(try
|
||||
(f ctx)
|
||||
(catch Exception e
|
||||
(assoc ctx :error e))))
|
||||
|
||||
(defn enter [ctx]
|
||||
(let [queue ^clojure.lang.PersistentQueue (:queue ctx)
|
||||
stack (:stack ctx)
|
||||
error (:error ctx)
|
||||
interceptor (peek queue)]
|
||||
(cond
|
||||
|
||||
;; all done
|
||||
(not interceptor)
|
||||
(leave ctx stack :leave)
|
||||
|
||||
;; error
|
||||
error
|
||||
(leave (assoc ctx :queue nil) stack :error)
|
||||
|
||||
;; continue
|
||||
:else
|
||||
(let [queue (pop queue)
|
||||
stack (conj stack interceptor)
|
||||
f (or (:enter interceptor) identity)
|
||||
ctx (-> ctx (assoc :queue queue) (assoc :stack stack) (try-f f))]
|
||||
(recur ctx)))))
|
||||
|
||||
(defrecord Context [queue stack request response])
|
||||
|
||||
(defn context [interceptors request]
|
||||
(->Context (into clojure.lang.PersistentQueue/EMPTY interceptors) nil request nil))
|
||||
|
||||
(defn execute [interceptors request]
|
||||
(enter (context interceptors request)))
|
||||
|
||||
(ns user)
|
||||
|
||||
(require '[reitit.http :as http])
|
||||
(require '[reitit.interceptor.simple :as simple])
|
||||
|
||||
(def i (fn [value]
|
||||
{:enter (fn [ctx]
|
||||
(update-in ctx [:request :enter] (fnil conj []) value))
|
||||
:leave (fn [ctx]
|
||||
(update-in ctx [:response :body :leave] (fnil conj []) value))}))
|
||||
|
||||
(def f (fn [key] {key (fn [ctx] (throw (ex-info "fail" {})))}))
|
||||
|
||||
(def app
|
||||
(http/http-handler
|
||||
(http/router
|
||||
["/api"
|
||||
{:interceptors [(i 1) (i 2)]}
|
||||
["/ipa"
|
||||
{:interceptors [(i 3) (f :enter) (i 4)]
|
||||
:handler (fn [{:keys [enter]}]
|
||||
{:status 200
|
||||
:body {:enter enter}})}]])
|
||||
simple/execute))
|
||||
|
||||
(app {:request-method :get, :uri "/api/ipa"})
|
||||
; => {:status 200, :body {:enter [1 2 3 4], :leave [4 3 2 1]}}
|
||||
|
||||
(def app2
|
||||
(http/http-handler
|
||||
(http/router
|
||||
["/api"
|
||||
{:interceptors [(i 1) (i 2)]}
|
||||
["/ipa"
|
||||
{:interceptors [(i 3) (i 4) (fn [ctx]
|
||||
(assoc ctx
|
||||
:response
|
||||
{:status 200
|
||||
:body {:enter (-> ctx
|
||||
:request
|
||||
:enter)}}))]}]])
|
||||
simple/execute))
|
||||
|
||||
(app2 {:request-method :get, :uri "/api/ipa"})
|
||||
; => {:status 200, :body {:enter [1 2 3 4], :leave [4 3 2 1]}}
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
(defrecord Methods [get head post put delete connect options trace patch])
|
||||
(defrecord Endpoint [data handler path method middleware])
|
||||
|
||||
(defn- group-keys [data]
|
||||
(defn ^:no-wiki group-keys [data]
|
||||
(reduce-kv
|
||||
(fn [[top childs] k v]
|
||||
(if (http-methods k)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
(s/def ::summary string?)
|
||||
(s/def ::description string?)
|
||||
|
||||
(s/def ::swagger (s/keys :req-un [::id]))
|
||||
(s/def ::swagger (s/keys :opt-un [::id]))
|
||||
(s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::summary ::description]))
|
||||
|
||||
(def swagger-feature
|
||||
|
|
|
|||
Loading…
Reference in a new issue