2017-09-18 05:30:03 +00:00
# Ring Router
2017-09-14 13:33:36 +00:00
2018-02-11 17:15:25 +00:00
[Ring ](https://github.com/ring-clojure/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.
2018-09-23 07:52:17 +00:00
Read more about the [Ring Concepts ](https://github.com/ring-clojure/ring/wiki/Concepts ).
2018-02-11 17:15:25 +00:00
```clj
2020-10-21 21:10:52 +00:00
[metosin/reitit-ring "0.5.10"]
2018-02-11 17:15:25 +00:00
```
2018-09-23 07:52:17 +00:00
## `reitit.ring/ring-router`
2018-02-11 17:15:25 +00:00
2018-09-23 07:52:17 +00:00
`ring-router` is a higher order router, which adds support for `:request-method` based routing, [handlers ](https://github.com/ring-clojure/ring/wiki/Concepts#handlers ) and [middleware ](https://github.com/ring-clojure/ring/wiki/Concepts#middleware ).
It accepts the following options:
2017-09-14 13:33:36 +00:00
2020-05-12 18:21:39 +00:00
| key | description |
| ----------------------------------------|-------------|
| `:reitit.middleware/transform` | Function of `[Middleware] => [Middleware]` to transform the expanded Middleware (default: identity).
| `:reitit.middleware/registry` | Map of `keyword => IntoMiddleware` to replace keyword references into Middleware
| `:reitit.ring/default-options-endpoint` | Default endpoint for `:options` method (default: default-options-endpoint)
2018-09-23 07:52:17 +00:00
Example router:
2017-09-14 13:33:36 +00:00
```clj
(require '[reitit.ring :as ring])
(defn handler [_]
{:status 200, :body "ok"})
2018-09-23 07:52:17 +00:00
(def router
(ring/router
["/ping" {:get handler}]))
```
2018-09-24 16:21:19 +00:00
Match contains `:result` compiled by the `ring-router` :
2018-09-23 07:52:17 +00:00
```clj
(require '[reitit.core :as r])
(r/match-by-path router "/ping")
;#Match{:template "/ping"
; :data {:get {:handler #object [...]}}
; :result #Methods {:get #Endpoint {...}
; :options #Endpoint {...}}
; :path-params {}
; :path "/ping"}
```
## `reitit.ring/ring-handler`
Given a `ring-router` , optional default-handler & options, `ring-handler` function will return a valid ring handler supporting both synchronous and [asynchronous ](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html ) request handling. The following options are available:
2019-01-14 19:53:37 +00:00
| key | description |
| ------------------|-------------|
| `:middleware` | Optional sequence of middleware that wrap the ring-handler"
| `:inject-match?` | Boolean to inject `match` into request under `:reitit.core/match` key (default true)
| `:inject-router?` | Boolean to inject `router` into request under `:reitit.core/router` key (default true)
2018-09-23 07:52:17 +00:00
Simple Ring app:
```clj
(def app (ring/ring-handler router))
2017-09-14 13:33:36 +00:00
```
Applying the handler:
```clj
(app {:request-method :get, :uri "/favicon.ico"})
; nil
2018-02-11 17:15:25 +00:00
```
2017-09-14 13:33:36 +00:00
2018-02-11 17:15:25 +00:00
```clj
2017-09-14 13:33:36 +00:00
(app {:request-method :get, :uri "/ping"})
; {:status 200, :body "ok"}
```
2018-09-23 07:52:17 +00:00
The router can be accessed via `get-router` :
2017-09-14 13:33:36 +00:00
```clj
2018-09-23 07:52:17 +00:00
(-> app (ring/get-router) (r/compiled-routes))
;[["/ping"
; {:handler #object [...]}
; #Methods {:get #Endpoint {:data {:handler #object [...]}
; :handler #object [...]
; :middleware []}
; :options #Endpoint {:data {:handler #object [...]}
; :handler #object [...]
; :middleware []}}]]
2017-09-14 13:33:36 +00:00
```
2017-09-18 05:30:03 +00:00
# Request-method based routing
2017-09-14 13:33:36 +00:00
2018-09-24 16:21:19 +00:00
Handlers can be placed either to the top-level (all methods) or under a specific method (`:get`, `:head` , `:patch` , `:delete` , `:options` , `:post` , `:put` or `:trace` ). Top-level handler is used if request-method based handler is not found.
2018-09-23 07:52:17 +00:00
By default, the `:options` route is generated for all paths - to enable thing like [CORS ](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing ).
2017-09-14 13:33:36 +00:00
```clj
(def app
(ring/ring-handler
(ring/router
2018-09-23 07:52:17 +00:00
[["/all" handler]
["/ping" {:name ::ping
:get handler
:post handler}]])))
```
Top-level handler catches all methods:
```clj
(app {:request-method :delete, :uri "/all"})
; {:status 200, :body "ok"}
```
2017-09-14 13:33:36 +00:00
2018-09-23 07:52:17 +00:00
Method-level handler catches only the method:
```clj
2017-09-14 13:33:36 +00:00
(app {:request-method :get, :uri "/ping"})
; {:status 200, :body "ok"}
(app {:request-method :put, :uri "/ping"})
; nil
```
2018-09-24 16:21:19 +00:00
By default, `:options` is also supported (see router options to change this):
2018-09-23 07:52:17 +00:00
```clj
(app {:request-method :options, :uri "/ping"})
; {:status 200, :body ""}
```
2017-10-30 06:46:20 +00:00
Name-based reverse routing:
2017-09-14 13:33:36 +00:00
```clj
(-> app
(ring/get-router)
2018-09-23 07:52:17 +00:00
(r/match-by-name ::ping)
(r/match->path))
2017-09-14 13:33:36 +00:00
; "/ping"
```
2017-09-18 05:30:03 +00:00
# Middleware
2017-09-14 13:33:36 +00:00
2018-09-24 16:21:19 +00:00
Middleware can be mounted using a `:middleware` key - either to top-level or under request method submap. Its value should be a vector of `reitit.middleware/IntoMiddleware` values. These include:
2017-09-14 13:33:36 +00:00
2017-10-30 06:46:20 +00:00
1. normal ring middleware function `handler -> request -> response`
2018-07-28 09:01:12 +00:00
2. vector of middleware function `[handler args*] -> request -> response` and it's arguments
3. a [data-driven middleware ](data_driven_middleware.md ) record or a map
4. a Keyword name, to lookup the middleware from a [Middleware Registry ](middleware_registry.md )
2017-09-14 13:33:36 +00:00
A middleware and a handler:
```clj
(defn wrap [handler id]
(fn [request]
(handler (update request ::acc (fnil conj []) id))))
2019-07-13 13:04:06 +00:00
(defn handler [{::keys [acc]}]
2017-09-14 13:33:36 +00:00
{:status 200, :body (conj acc :handler)})
```
App with nested middleware:
```clj
(def app
(ring/ring-handler
(ring/router
2018-02-11 17:15:25 +00:00
;; a middleware function
2017-09-14 13:33:36 +00:00
["/api" {:middleware [#(wrap % :api)]}
["/ping" handler]
2018-02-11 17:15:25 +00:00
;; a middleware vector at top level
2017-09-14 13:33:36 +00:00
["/admin" {:middleware [[wrap :admin]]}
["/db" {:middleware [[wrap :db]]
2018-02-11 17:15:25 +00:00
;; a middleware vector at under a method
2017-10-30 06:46:20 +00:00
:delete {:middleware [[wrap :delete]]
2017-09-14 13:33:36 +00:00
:handler handler}}]]])))
```
Middleware is applied correctly:
```clj
(app {:request-method :delete, :uri "/api/ping"})
; {:status 200, :body [:api :handler]}
```
```clj
(app {:request-method :delete, :uri "/api/admin/db"})
; {:status 200, :body [:api :admin :db :delete :handler]}
```
2018-09-22 18:41:54 +00:00
Top-level middleware, applied before any routing is done:
```clj
(def app
(ring/ring-handler
(ring/router
["/api" {:middleware [[mw :api]]}
["/get" {:get handler}]])
nil
{:middleware [[mw :top]]}))
(app {:request-method :get, :uri "/api/get"})
; {:status 200, :body [:top :api :ok]}
```