InterceptorsInterceptors
Into the Core of PedestalInto the Core of Pedestal
カマイルカ /laʒenɔʁɛ̃k/カマイルカ /laʒenɔʁɛ̃k/
lagénorhynquelagénorhynque
(defprofile lagénorhynque
:name "Kent OHASHI"
:languages [Clojure Common-Lisp Scheme Haskell
English français]
:interests [programming language-learning law mathematics]
:contributing [github.com/japan-clojurians/clojure-site-ja])
1. Introduction to Pedestal
2. Ring "middleware"
3. Pedestal "interceptor"
Introduction to PedestalIntroduction to Pedestal
What isWhat is ??
cf. : client-side (no longer developed)
PedestalPedestal
Pedestal is a set of libraries written in
Clojure that aims to bring both the
language and its principles (Simplicity,
Power and Focus) to server-side
development.
pedestal-app
Create a Pedestal projectCreate a Pedestal project
cf.
$ lein new pedestal-service hello-pedestal
Generating a pedestal-service application called hello-pedestal.
$ cd hello-pedestal/
lagenorhynque/hello-pedestal
$ tree .
.
├── Capstanfile
├── Dockerfile
├── README.md
├── config
│ └── logback.xml
├── project.clj
├── src
│ └── hello_pedestal
│ ├── server.clj
│ └── service.clj
└── test
└── hello_pedestal
└── service_test.clj
Run the serverRun the server
$ lein run
INFO org.eclipse.jetty.util.log - Logging initialized @16865ms
to org.eclipse.jetty.util.log.Slf4jLog
Creating your server...
INFO org.eclipse.jetty.server.Server - jetty-9.4.10.v20180503;
built: 2018-05-03T15:56:21.710Z; git: daa59876e6f384329b122929e7
0a80934569428c; jvm 10.0.2+13
INFO o.e.j.server.handler.ContextHandler - Started o.e.j.s.Ser
vletContextHandler@16768389{/,null,AVAILABLE}
INFO o.e.jetty.server.AbstractConnector - Started ServerConnec
tor@22531d51{HTTP/1.1,[http/1.1, h2c]}{localhost:8080}
INFO org.eclipse.jetty.server.Server - Started @17463ms
GET http://localhost:8080GET http://localhost:8080
$ curl -i "http://localhost:8080"
HTTP/1.1 200 OK
Date: Tue, 25 Sep 2018 08:41:25 GMT
Strict-Transport-Security: max-age=31536000; includeSubdomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Content-Security-Policy: object-src 'none'; script-src 'unsafe-i
nline' 'unsafe-eval' 'strict-dynamic' https: http:;
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Hello World!
home­pagehome­page handlerhandler
src/hello_pedestal/service.clj#L13-L16
(defn home-page
[request]
(println request)
(ring-resp/response "Hello World!"))
GET http://localhost:8080/aboutGET http://localhost:8080/about
$ curl -i "http://localhost:8080/about"
HTTP/1.1 200 OK
Date: Tue, 25 Sep 2018 08:41:43 GMT
Strict-Transport-Security: max-age=31536000; includeSubdomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Content-Security-Policy: object-src 'none'; script-src 'unsafe-i
nline' 'unsafe-eval' 'strict-dynamic' https: http:;
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Clojure 1.9.0 - served from /about
about­pageabout­page handlerhandler
src/hello_pedestal/service.clj#L7-L11
(defn about-page
[request]
(ring-resp/response (format "Clojure %s - served from %s"
(clojure-version)
(route/url-for ::about-page))))
routesroutes
src/hello_pedestal/service.clj#L18-L25
;; Defines "/" and "/about" routes with their associated :get ha
;; ndlers.
;; The interceptors defined after the verb map (e.g., {:get home
;; -page}
;; apply to / and its children (/about).
(def common-interceptors [(body-params/body-params)
http/html-body])
;; Tabular routes
(def routes #{["/" :get (conj common-interceptors
`home-page)]
["/about" :get (conj common-interceptors
`about-page)]})
Ring "middleware"Ring "middleware"
request (map)request (map)
spec
{:server-port 8080
:server-name "localhost"
:remote-addr "127.0.0.1"
:uri "/hello"
:query-string "name=World"
:scheme :http
:request-method :get
:headers {"Accept" "application/json"}
:body nil}
(s/def ::request
(s/keys :opt-un [::server-port ::server-name ::remote-addr
::uri ::query-string ::scheme
::request-method ::headers ::body
,,,]))
response (map)response (map)
spec
{:status 200
:headers {"Content-Type" "application/json"}
:body {:greeting "Hello, World!"}}
(s/def ::response
(s/keys :opt-un [::status ::headers ::body]))
handler (function)handler (function)
spec
cf. asynchronous handler
(defn hello [request]
(let [name (get-in request [:params :name])]
{:status 200
:headers {"Content-Type" "application/json"}
:body {:greeting (str "Hello, " name "!")}}))
(s/def ::handler
(s/fspec :args (s/cat :request ::request)
:ret ::response))
middlewaremiddleware
cf.
(defn wrap-keyword-params* [handler]
(fn [request]
(handler (update request :params
#(->> %
(map (fn [[k v]] [(keyword k) v]))
(into {}))))))
ring.middleware.keyword-params
spec
template
cf. asynchronous middleware
(s/def ::middleware
(s/fspec :args (s/cat :handler ::handler)
:ret ::handler))
(defn some-middleware [handler]
(fn [request]
(let [response (handler (f request))]
(g response))))
Ring middlewareRing middleware
Pedestal "interceptors"Pedestal "interceptors"
context (map)context (map)
spec
{:request {:protocol "HTTP/1.1", :async-supported? true,
:remote-addr "127.0.0.1", ,,,},
:response nil,
:io.pedestal.interceptor.chain/terminators (#object[io.pedestal
.http.impl.servlet_interceptor$terminator_inject$fn__15706 0x257
66941 "io.pedestal.http.impl.servlet_interceptor$terminator_inje
ct$fn__15706@25766941"]),
:io.pedestal.interceptor.chain/queue #object[clojure.lang.Persi
stentQueue 0x37f89872 "clojure.lang.PersistentQueue@72c17787"],
,,,}
(s/def ::context
(s/keys :opt-un [::request ::response]
:opt [:io.pedestal.interceptor.chain/queue
:io.pedestal.interceptor.chain/terminators
,,,]))
interceptorinterceptor
cf.
(def keyword-params*
{:name ::keyword-params*
:enter (fn [context]
(update-in context [:request :params]
#(->> %
(map (fn [[k v]] [(keyword k) v]))
(into {}))))})
io.pedestal.http.params
spec
template
cf. function returning a core.async channel
(s/def ::enter
(s/fspec :args (s/cat :context ::context)
:ret ::context))
(s/def ::leave
(s/fspec :args (s/cat :context ::context)
:ret ::context))
(s/def ::interceptor
(s/keys :opt-un [::name ::enter ::leave ::error]))
(def some-interceptor
{:name ::some-interceptor
:enter (fn [context]
(f context))
:leave (fn [context]
(g context))})
Pedestal interceptorsPedestal interceptors
e.g.e.g.
src/hello_pedestal/service.clj
lagenorhynque/hello-pedestallagenorhynque/hello-pedestal
(def common-interceptors [(body-params/body-params) http/html-body
(def routes #{["/" :get (conj common-interceptors `home-page)]
["/about" :get (conj common-interceptors `about-pag
e.g.e.g. lagenorhynque/js-frameworks (chat-server)lagenorhynque/js-frameworks (chat-server)
(defmethod ig/init-key ::routes
[_ {:keys [db redis]}]
(let [common-interceptors [(body-params/body-params)
http/json-body
(interceptor/store-session redis)
interceptor/authenticate
interceptor/attach-tx-data
(interceptor/validate validation-sch
(interceptor/attach-database db)]
auth-interceptors [(body-params/body-params)
http/json-body
(interceptor/store-session redis)
interceptor/attach-tx-data
(interceptor/validate validation-schem
(interceptor/attach-database db)]]
src/clj/chat_server/routes.clj
#(route/expand-routes
#{["/api/authentication" :get
(conj common-interceptors
`authentication/fetch-user)]
["/api/authentication" :post
(conj auth-interceptors
`authentication/login)]
["/api/authentication" :delete
(conj auth-interceptors
`authentication/logout)]
["/api/channels" :get (conj common-interceptors
`channels/list-channels)]
["/api/channels" :post (conj common-interceptors
`channels/create-channel)]
,,,})))
Further ReadingFurther Reading
PedestalPedestal
example codeexample code
Pedestal
GitHub
Microservices with Clojure
lagenorhynque/hello-pedestal
lagenorhynque/js-frameworks (chat-server)
interceptorsinterceptors
middlewaremiddleware
Interceptors
Why interceptors? - quanttype
Concepts (Middleware) · ring-clojure/ring Wiki
ring/SPEC · ring-clojure/ring
Asynchronous Ring - Boolean Knot
other librariesother libraries
cf.
reitit
sieppari
Reitit, Data-Driven Routing with Clojure(Script)
- Metosin
Welcome Reitit 0.2.0! - Metosin
re-frame

Interceptors: Into the Core of Pedestal