Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows web applications to be constructed of modular components that can be shared among a variety of applications, web servers, and web frameworks.
[metosin/reitit-ring "0.1.0-SNAPSHOT"]
Ring-router adds support for handlers, middleware and routing based on :request-method
. Ring-router is created with reitit.ring/router
function. It uses a custom route compiler, creating a optimized data structure for handling route matches, with compiled middleware chain & handlers for all request methods. It also ensures that all routes have a :handler
defined. reitit.ring/ring-handler
is used to create a Ring handler out of ring-router.
Simple Ring app:
(require '[reitit.ring :as ring])
(defn handler [_]
{:status 200, :body "ok"})
(def app
(ring/ring-handler
(ring/router
["/ping" handler])))
Applying the handler:
(app {:request-method :get, :uri "/favicon.ico"})
; nil
(app {:request-method :get, :uri "/ping"})
; {:status 200, :body "ok"}
The expanded routes shows the compilation results:
(-> app (ring/get-router) (reitit/routes))
; [["/ping"
; {:handler #object[...]}
; #Methods{:any #Endpoint{:data {:handler #object[...]},
; :handler #object[...],
; :middleware []}}]]
Note the compiled resuts as third element in the route vector.
Handler are also looked under request-method keys: :get
, :head
, :patch
, :delete
, :options
, :post
or :put
. Top-level handler is used if request-method based handler is not found.
(def app
(ring/ring-handler
(ring/router
["/ping" {:name ::ping
:get handler
:post handler}])))
(app {:request-method :get, :uri "/ping"})
; {:status 200, :body "ok"}
(app {:request-method :put, :uri "/ping"})
; nil
Name-based reverse routing:
(-> app
(ring/get-router)
(reitit/match-by-name ::ping)
:path)
; "/ping"
Middleware can be added with a :middleware
key, either to top-level or under :request-method
submap. It's value should be a vector value of the following:
handler -> request -> response
handler ?args -> request -> response
and optinally it's args.A middleware and a handler:
(defn wrap [handler id]
(fn [request]
(handler (update request ::acc (fnil conj []) id))))
(defn handler [{:keys [::acc]}]
{:status 200, :body (conj acc :handler)})
App with nested middleware:
(def app
(ring/ring-handler
(ring/router
;; a middleware function
["/api" {:middleware [#(wrap % :api)]}
["/ping" handler]
;; a middleware vector at top level
["/admin" {:middleware [[wrap :admin]]}
["/db" {:middleware [[wrap :db]]
;; a middleware vector at under a method
:delete {:middleware [[wrap :delete]]
:handler handler}}]]])))
Middleware is applied correctly:
(app {:request-method :delete, :uri "/api/ping"})
; {:status 200, :body [:api :handler]}
(app {:request-method :delete, :uri "/api/admin/db"})
; {:status 200, :body [:api :admin :db :delete :handler]}
By default, if no routes match, nil
is returned, which is not valid response in Ring:
(defn handler [_]
{:status 200, :body ""})
(def app
(ring/ring-handler
(ring/router
["/ping" handler])))
(app {:uri "/invalid"})
; nil
Setting the default-handler as a second argument to ring-handler
:
(def app
(ring/ring-handler
(ring/router
["/ping" handler])
(constantly {:status 404, :body ""})))
(app {:uri "/invalid"})
; {:status 404, :body ""}
To get more correct http error responses, ring/create-default-handler
can be used. It differentiates :not-found
(no route matched), :method-not-accepted
(no method matched) and :not-acceptable
(handler returned nil
).
With defaults:
(def app
(ring/ring-handler
(ring/router
[["/ping" {:get handler}]
["/pong" (constantly nil)]])
(ring/create-default-handler)))
(app {:request-method :get, :uri "/ping"})
; {:status 200, :body ""}
(app {:request-method :get, :uri "/"})
; {:status 404, :body ""}
(app {:request-method :post, :uri "/ping"})
; {:status 405, :body ""}
(app {:request-method :get, :uri "/pong"})
; {:status 406, :body ""}
With custom responses:
(def app
(ring/ring-handler
(ring/router
[["/ping" {:get handler}]
["/pong" (constantly nil)]])
(ring/create-default-handler
{:not-found (constantly {:status 404, :body "kosh"})
:method-not-allowed (constantly {:status 405, :body "kosh"})
:not-acceptable (constantly {:status 406, :body "kosh"})})))
(app {:request-method :get, :uri "/ping"})
; {:status 200, :body ""}
(app {:request-method :get, :uri "/"})
; {:status 404, :body "kosh"}
(app {:request-method :post, :uri "/ping"})
; {:status 405, :body "kosh"}
(app {:request-method :get, :uri "/pong"})
; {:status 406, :body "kosh"}
All built-in middleware provide both 2 and 3-arity and are compiled for both Clojure & ClojureScript, so they work with Async Ring and Node.js too.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close