Reitit-pedestal

This commit is contained in:
Tommi Reiman 2018-12-26 15:43:26 +02:00
parent 31e3eb43aa
commit 8e0aaf7ae8
19 changed files with 167 additions and 127 deletions

View file

@ -7,7 +7,7 @@ A fast data-driven router for Clojure(Script).
* First-class [route data](https://metosin.github.io/reitit/basics/route_data.html)
* Bi-directional routing
* [Pluggable coercion](https://metosin.github.io/reitit/coercion/coercion.html) ([schema](https://github.com/plumatic/schema) & [clojure.spec](https://clojure.org/about/spec))
* Helpers for [ring](https://metosin.github.io/reitit/ring/ring.html), [http](https://metosin.github.io/reitit/http/interceptors.html) & [the frontend](https://metosin.github.io/reitit/frontend/basics.html)
* Helpers for [ring](https://metosin.github.io/reitit/ring/ring.html), [http](https://metosin.github.io/reitit/http/interceptors.html), [pedestal](http://pedestal.io) & [frontend](https://metosin.github.io/reitit/frontend/basics.html)
* Extendable
* Modular
* [Fast](https://metosin.github.io/reitit/performance.html)
@ -21,7 +21,7 @@ See the [full documentation](https://metosin.github.io/reitit/) for details.
There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians Slack](http://clojurians.net/) for discussion & help.
## Modules
## Main Modules
* `reitit` - all bundled
* `reitit-core` - the routing core
@ -36,9 +36,13 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
* `reitit-interceptors` - [common interceptors](https://metosin.github.io/reitit/http/default_interceptors.html)
* `reitit-sieppari` support for [Sieppari](https://github.com/metosin/sieppari)
## Extra modules
* `reitit-pedestal` support for [Pedestal](http://pedestal.io)
## Latest version
All bundled:
All main modules bundled:
```clj
[metosin/reitit "0.2.9"]
@ -70,6 +74,11 @@ Optionally, the parts can be required separately:
[metosin/reitit-sieppari "0.2.9"]
```
```clj
;; pedestal
[metosin/reitit-pedestal "0.2.9"]
```
## Quick start
```clj

View file

@ -1,25 +1,70 @@
# Pedestal
[Pedestal](http://pedestal.io/) is a well known interceptor-based web framework for Clojure. To use `reitit-http` with Pedestal, we need to change the default routing interceptor into a new one. Examples projects show how to do this.
[Pedestal](http://pedestal.io/) is a well known interceptor-based web framework for Clojure. To use `reitit-http` with Pedestal, we need to change the default routing interceptor. The needed helpers for this are found in a separate package:
```clj
[metosin/reitit-ring "0.2.9"]
```
You should read the [interceptor guide](interceptors.md) to understand the basics on Interceptor-based dispatch.
## Example
A minimalistic example on how to to swap the default-router with a reitit router.
```clj
; [io.pedestal/pedestal.service "0.5.5"]
; [io.pedestal/pedestal.jetty "0.5.5"]
; [metosin/reitit-pedestal "0.2.9"]
; [metosin/reitit "0.2.9"]
(ns example.server
(:require [io.pedestal.http :as server]
[reitit.pedestal :as pedestal]
[reitit.http :as http]
[reitit.ring :as ring]))
(def router
(pedestal/routing-interceptor
(http/router
["/ping" (fn [_] {:status 200, :body "pong"})])
(ring/create-default-handler)))
(defn start []
(-> {::server/type :jetty
::server/port 3000
::server/join? false
;; no pedestal routes
::server/routes []}
(server/default-interceptors)
;; swap the reitit router
(pedestal/replace-last-interceptor router)
(server/dev-interceptors)
(server/create-server)
(server/start))
(println "server running in port 3000"))
(start)
```
## Caveat
`reitit-http` defines Interceptors as `reitit.interceptor/Interceptor`. Compared to Pedestal 2-arity error handlers, reitit uses a simplified (1-arity) handlers. Differences in error handling are described in the [Sieppari README](https://github.com/metosin/sieppari#differences-to-pedestal).
There is no common interceptor spec for Clojure and All default reitit interceptors (coercion, exceptions etc.) use the [Sieppari](https://github.com/metosin/sieppari) interceptor model. For most parts, they are fully compatible with the Pedestal Interceptor model. Only exception being that the `:error` handlers take just 1 arity (`context`) compared to [Pedestal's 2-arity](http://pedestal.io/reference/error-handling) (`context` and `exception`).
* you can use any [pedestal-style interceptor](http://pedestal.io/reference/interceptors) within reitit router (as Pedestal is executing those anyway)
* you can use any reitit-style interceptor that doesn't have `:error`-stage defined
* using a reitit-style interceptor with `:error` defined will cause `ArityException` if invoked
Currently, there is only the `reitit.http.interceptors.exception/exception-interceptor` which has `:error` defined - just don't use it and everything should just work.
You are most welcome to discuss about a common interceptor spec in [#interceptors](https://clojurians.slack.com/messages/interceptors/) in [Clojurians Slack](http://clojurians.net/).
See the [error handling guide](http://pedestal.io/reference/error-handling) on how to handle errors with Pedestal.
## Examples
## More examples
### Simple
* simple example, with both sync & async code:
* https://github.com/metosin/reitit/tree/master/examples/pedestal
Simple example, with both sync & async interceptors: https://github.com/metosin/reitit/tree/master/examples/pedestal
### With batteries
### Swagger
* with [default interceptors](default_interceptors.md), [coercion](../coercion/coercion.md) and [swagger](../ring/swagger.md)-support (note: exception handling is disabled):
* https://github.com/metosin/reitit/tree/master/examples/pedestal-swagger
More complete example with custom interceptors, [default interceptors](default_interceptors.md), [coercion](../coercion/coercion.md) and [swagger](../ring/swagger.md)-support: https://github.com/metosin/reitit/tree/master/examples/pedestal-swagger
note: exception handling is disabled in this example

View file

@ -4,7 +4,7 @@
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[ring-server "0.5.0"]
[reagent "0.8.1"]
[ring "1.6.3"]

View file

@ -4,7 +4,7 @@
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[ring-server "0.5.0"]
[reagent "0.8.1"]
[ring "1.6.3"]

View file

@ -1,6 +1,6 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit Http App with Swagger"
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.0"]
[aleph "0.4.6"]
[metosin/reitit "0.2.9"]]

View file

@ -1,6 +1,6 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit Ring App with Swagger"
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[org.clojure/core.async "0.4.474"]
[funcool/promesa "1.9.0"]
[manifold "0.1.8"]

View file

@ -1,6 +1,6 @@
(defproject just-coercion-with-ring "0.1.0-SNAPSHOT"
:description "Reitit coercion with vanilla ring"
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.0"]
[metosin/muuntaja "0.4.1"]
[metosin/reitit "0.2.9"]])

View file

@ -1,7 +1,8 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit-http with pedestal"
:dependencies [[org.clojure/clojure "1.9.0"]
[io.pedestal/pedestal.service "0.5.4"]
[io.pedestal/pedestal.jetty "0.5.4"]
:dependencies [[org.clojure/clojure "1.10.0"]
[io.pedestal/pedestal.service "0.5.5"]
[io.pedestal/pedestal.jetty "0.5.5"]
[metosin/reitit-pedestal "0.2.9"]
[metosin/reitit "0.2.9"]]
:repl-options {:init-ns example.server})

View file

@ -1,6 +1,6 @@
(ns example.server
(:require [io.pedestal.http]
[reitit.interceptor.pedestal :as pedestal]
(:require [io.pedestal.http :as server]
[reitit.pedestal :as pedestal]
[reitit.ring :as ring]
[reitit.http :as http]
[reitit.swagger :as swagger]
@ -9,12 +9,15 @@
[reitit.coercion.spec :as spec-coercion]
[reitit.http.interceptors.parameters :as parameters]
[reitit.http.interceptors.muuntaja :as muuntaja]
#_[reitit.http.interceptors.exception :as exception]
[reitit.http.interceptors.multipart :as multipart]
[muuntaja.core :as m]
[clojure.java.io :as io]))
[clojure.core.async :as a]
[clojure.java.io :as io]
[muuntaja.core :as m]))
(def routing-interceptor
(defn interceptor [number]
{:enter (fn [ctx] (a/go (update-in ctx [:request :number] (fnil + 0) number)))})
(def router
(pedestal/routing-interceptor
(http/router
[["/swagger.json"
@ -23,6 +26,17 @@
:description "with pedestal & reitit-http"}}
:handler (swagger/create-swagger-handler)}}]
["/interceptors"
{:swagger {:tags ["interceptors"]}
:interceptors [(interceptor 1)]}
["/number"
{:interceptors [(interceptor 10)]
:get {:interceptors [(interceptor 100)]
:handler (fn [req]
{:status 200
:body (select-keys req [:number])})}}]]
["/files"
{:swagger {:tags ["files"]}}
@ -69,8 +83,6 @@
(muuntaja/format-negotiate-interceptor)
;; encoding response body
(muuntaja/format-response-interceptor)
;; exception handling - doesn't work
;;(exception/exception-interceptor)
;; decoding request body
(muuntaja/format-request-interceptor)
;; coercing response bodys
@ -85,27 +97,27 @@
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil}})
(ring/create-resource-handler)
(ring/create-default-handler))))
(defonce server (atom nil))
(defn start []
(when @server
(io.pedestal.http/stop @server)
(println "server stopped"))
(-> {:env :prod
:io.pedestal.http/routes []
:io.pedestal.http/resource-path "/public"
:io.pedestal.http/type :jetty
:io.pedestal.http/port 3000}
(merge {:env :dev
:io.pedestal.http/join? false
:io.pedestal.http/allowed-origins {:creds true :allowed-origins (constantly true)}})
(pedestal/default-interceptors routing-interceptor)
io.pedestal.http/dev-interceptors
io.pedestal.http/create-server
io.pedestal.http/start
(->> (reset! server)))
(-> {:env :dev
::server/type :jetty
::server/port 3000
::server/join? false
;; no pedestal routes
::server/routes []
;; allow serving the swagger-ui styles & scripts from self
::server/secure-headers {:content-security-policy-settings
{:default-src "'self'"
:style-src "'self' 'unsafe-inline'"
:script-src "'self' 'unsafe-inline'"}}}
(io.pedestal.http/default-interceptors)
;; use the reitit router
(pedestal/replace-last-interceptor router)
(io.pedestal.http/dev-interceptors)
(io.pedestal.http/create-server)
(io.pedestal.http/start))
(println "server running in port 3000"))
(comment

View file

@ -1,7 +1,8 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit-http with pedestal"
:dependencies [[org.clojure/clojure "1.9.0"]
[io.pedestal/pedestal.service "0.5.4"]
[io.pedestal/pedestal.jetty "0.5.4"]
:dependencies [[org.clojure/clojure "1.10.0"]
[io.pedestal/pedestal.service "0.5.5"]
[io.pedestal/pedestal.jetty "0.5.5"]
[metosin/reitit-pedestal "0.2.9"]
[metosin/reitit "0.2.9"]]
:repl-options {:init-ns example.server})

View file

@ -1,7 +1,7 @@
(ns example.server
(:require [io.pedestal.http]
(:require [io.pedestal.http :as server]
[clojure.core.async :as a]
[reitit.interceptor.pedestal :as pedestal]
[reitit.pedestal :as pedestal]
[muuntaja.interceptor]
[reitit.http :as http]
[reitit.ring :as ring]))
@ -18,7 +18,7 @@
{:enter (fn [{:keys [request] :as ctx}]
(a/go (assoc ctx :response (handler request))))})
(def routing-interceptor
(def router
(pedestal/routing-interceptor
(http/router
["/api"
@ -36,32 +36,27 @@
;; optional interceptors for all matched routes
{:data {:interceptors [(interceptor :router)]}})
;; optional default ring handler (if no routes have matched)
(ring/create-default-handler)
;; optional default ring handlers (if no routes have matched)
(ring/routes
(ring/create-resource-handler)
(ring/create-default-handler))
;; optional top-level routes for both routes & default route
{:interceptors [(muuntaja.interceptor/format-interceptor)
(interceptor :top)]}))
(defonce server (atom nil))
(defn start []
(when @server
(io.pedestal.http/stop @server)
(println "server stopped"))
(-> {:env :prod
:io.pedestal.http/routes []
:io.pedestal.http/resource-path "/public"
:io.pedestal.http/type :jetty
:io.pedestal.http/port 3000}
(merge {:env :dev
:io.pedestal.http/join? false
:io.pedestal.http/allowed-origins {:creds true :allowed-origins (constantly true)}})
(pedestal/default-interceptors routing-interceptor)
io.pedestal.http/dev-interceptors
io.pedestal.http/create-server
io.pedestal.http/start
(->> (reset! server)))
(-> {::server/type :jetty
::server/port 3000
::server/join? false
;; no pedestal routes
::server/routes []}
(server/default-interceptors)
;; use the reitit router
(pedestal/replace-last-interceptor router)
(server/dev-interceptors)
(server/create-server)
(server/start))
(println "server running in port 3000"))
(comment

View file

@ -1,38 +0,0 @@
(ns reitit.interceptor.pedestal
(:require [io.pedestal.interceptor.chain :as chain]
[io.pedestal.interceptor :as interceptor]
[io.pedestal.http :as http]
[reitit.interceptor]
[reitit.http])
(:import (reitit.interceptor Executor)))
(def pedestal-executor
(reify
Executor
(queue [_ interceptors]
(->> interceptors
(map (fn [{:keys [::interceptor/handler] :as interceptor}]
(or handler interceptor)))
(map interceptor/interceptor)))
(enqueue [_ context interceptors]
(chain/enqueue context interceptors))))
(defn routing-interceptor
([router]
(routing-interceptor router nil))
([router default-handler]
(routing-interceptor router default-handler nil))
([router default-handler {:keys [interceptors]}]
(interceptor/interceptor
(reitit.http/routing-interceptor
router
default-handler
{:executor pedestal-executor
:interceptors interceptors}))))
(defn default-interceptors [spec router]
(-> spec
(assoc ::http/routes [])
(http/default-interceptors)
(update ::http/interceptors (comp vec butlast))
(update ::http/interceptors conj router)))

View file

@ -1,6 +1,6 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit Ring App"
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.0"]
[metosin/reitit "0.2.9"]]
:repl-options {:init-ns example.server})

View file

@ -1,6 +1,6 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit Ring App with Swagger"
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.0"]
[metosin/reitit "0.2.9"]]
:repl-options {:init-ns example.server})

View file

@ -1,6 +1,6 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit Ring App with Swagger"
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.0"]
[metosin/reitit "0.2.9"]]
:repl-options {:init-ns example.server})

View file

@ -0,0 +1,11 @@
(defproject metosin/reitit-pedestal "0.2.9"
:description "Reitit + Pedestal Integration"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:scm {:name "git"
:url "https://github.com/metosin/reitit"}
:plugins [[lein-parent "0.3.2"]]
:parent-project {:path "../../project.clj"
:inherit [:deploy-repositories :managed-dependencies]}
:dependencies [[metosin/reitit-http]])

View file

@ -1,4 +1,4 @@
(ns reitit.interceptor.pedestal
(ns reitit.pedestal
(:require [io.pedestal.interceptor.chain :as chain]
[io.pedestal.interceptor :as interceptor]
[io.pedestal.http :as http]
@ -19,9 +19,9 @@
(defn routing-interceptor
([router]
(routing-interceptor router nil))
(routing-interceptor router nil))
([router default-handler]
(routing-interceptor router default-handler nil))
(routing-interceptor router default-handler nil))
([router default-handler {:keys [interceptors]}]
(interceptor/interceptor
(reitit.http/routing-interceptor
@ -30,9 +30,7 @@
{:executor pedestal-executor
:interceptors interceptors}))))
(defn default-interceptors [spec router]
(-> spec
(assoc ::http/routes [])
(http/default-interceptors)
(update ::http/interceptors (comp vec butlast))
(update ::http/interceptors conj router)))
(defn replace-last-interceptor [service-map interceptor]
(-> service-map
(update ::http/interceptors pop)
(update ::http/interceptors conj interceptor)))

View file

@ -22,6 +22,7 @@
[metosin/reitit-swagger-ui "0.2.9"]
[metosin/reitit-frontend "0.2.9"]
[metosin/reitit-sieppari "0.2.9"]
[metosin/reitit-pedestal "0.2.9"]
[meta-merge "1.0.0"]
[lambdaisland/deep-diff "0.0-25"]
[ring/ring-core "1.7.1"]
@ -53,9 +54,10 @@
"modules/reitit-swagger/src"
"modules/reitit-swagger-ui/src"
"modules/reitit-frontend/src"
"modules/reitit-sieppari/src"]
"modules/reitit-sieppari/src"
"modules/reitit-pedestal/src"]
:dependencies [[org.clojure/clojure "1.9.0"]
:dependencies [[org.clojure/clojure "1.10.0"]
[org.clojure/clojurescript "1.10.439"]
;; modules dependencies
@ -76,12 +78,15 @@
[org.clojure/tools.namespace "0.2.11"]
[com.gfredericks/test.chuck "0.2.9"]
[io.pedestal/pedestal.service "0.5.5"]
[org.clojure/core.async "0.4.490"]
[manifold "0.1.8"]
[funcool/promesa "1.9.0"]
;; https://github.com/bensu/doo/issues/180
[fipp "0.6.14" :exclusions [org.clojure/core.rrb-vector]]]}
:1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]}
:perf {:jvm-opts ^:replace ["-server"
"-Xmx4096m"
"-Dclojure.compiler.direct-linking=true"]
@ -105,7 +110,7 @@
"-XX:+PrintCompilation"
"-XX:+UnlockDiagnosticVMOptions"
"-XX:+PrintInlining"]}}
:aliases {"all" ["with-profile" "dev,default"]
:aliases {"all" ["with-profile" "dev,default:dev,default,1.9"]
"perf" ["with-profile" "default,dev,perf"]
"test-clj" ["all" "do" ["bat-test"] ["check"]]
"test-browser" ["doo" "chrome-headless" "test"]

View file

@ -15,6 +15,7 @@ for ext in \
reitit-swagger-ui \
reitit-frontend \
reitit-sieppari \
reitit-pedestal \
reitit; do
cd modules/$ext; lein "$@"; cd ../..;
done