mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 00:11:11 +00:00
175 lines
4.7 KiB
Markdown
175 lines
4.7 KiB
Markdown
# Route Data
|
|
|
|
Route data is the key feature of reitit. Routes can have any map-like data attached to them, to be interpreted by the client application, `Router` or routing components like `Middleware` or `Interceptors`.
|
|
|
|
```clj
|
|
[["/ping" {:name ::ping}]
|
|
["/pong" {:handler identity}]
|
|
["/users" {:get {:roles #{:admin}
|
|
:handler identity}}]]
|
|
```
|
|
|
|
Besides map-like data, raw routes can have any non-sequential route argument after the path. This argument is expanded by `Router` (via `:expand` option) into route data at router creation time.
|
|
|
|
By default, Keywords are expanded into `:name` (see [Name-based Routing](./name_based_routing.md))
|
|
and functions into `:handler` keys.
|
|
|
|
```clj
|
|
(require '[reitit.core :as r])
|
|
|
|
(def router
|
|
(r/router
|
|
[["/ping" ::ping]
|
|
["/pong" identity]
|
|
["/users" {:get {:roles #{:admin}
|
|
:handler identity}}]]))
|
|
```
|
|
|
|
## Using Route Data
|
|
|
|
Expanded route data can be retrieved from a router with `routes` and is returned with `match-by-path` and `match-by-name` in case of a route match.
|
|
|
|
```clj
|
|
(r/routes router)
|
|
; [["/ping" {:name ::ping}]
|
|
; ["/pong" {:handler identity]}
|
|
; ["/users" {:get {:roles #{:admin}
|
|
; :handler identity}}]]
|
|
```
|
|
|
|
```clj
|
|
(r/match-by-path router "/ping")
|
|
; #Match{:template "/ping"
|
|
; :data {:name :user/ping}
|
|
; :result nil
|
|
; :path-params {}
|
|
; :path "/ping"}
|
|
```
|
|
|
|
```clj
|
|
(r/match-by-name router ::ping)
|
|
; #Match{:template "/ping"
|
|
; :data {:name :user/ping}
|
|
; :result nil
|
|
; :path-params {}
|
|
; :path "/ping"}
|
|
```
|
|
|
|
## Nested Route Data
|
|
|
|
For nested route trees, route data is accumulated recursively from root towards leafs using [meta-merge](https://github.com/weavejester/meta-merge). Default behavior for collections is `:append`, but this can be overridden to `:prepend`, `:replace` or `:displace` using the target meta-data.
|
|
|
|
An example router with nested data:
|
|
|
|
```clj
|
|
(def router
|
|
(r/router
|
|
["/api" {:interceptors [::api]}
|
|
["/ping" ::ping]
|
|
["/admin" {:roles #{:admin}}
|
|
["/users" ::users]
|
|
["/db" {:interceptors [::db]
|
|
:roles ^:replace #{:db-admin}}]]]))
|
|
```
|
|
|
|
Resolved route tree:
|
|
|
|
```clj
|
|
(r/routes router)
|
|
; [["/api/ping" {:interceptors [::api]
|
|
; :name :user/ping}]
|
|
; ["/api/admin/users" {:interceptors [::api]
|
|
; :roles #{:admin}
|
|
; :name ::users}]
|
|
; ["/api/admin/db" {:interceptors [::api ::db]
|
|
; :roles #{:db-admin}}]]
|
|
```
|
|
|
|
See also [nested parameter definitions for coercions](../ring/coercion.md#nested-parameter-definitions)
|
|
|
|
## Route Data Fragments
|
|
|
|
Just like [fragments in React.js](https://reactjs.org/docs/fragments.html), we can create routing tree fragments by using empty path `""`. This allows us to add route data without accumulating to path.
|
|
|
|
Given a route tree:
|
|
|
|
```clj
|
|
[["/swagger.json" ::swagger]
|
|
["/api-docs" ::api-docs]
|
|
["/api/ping" ::ping]
|
|
["/api/pong" ::pong]]
|
|
```
|
|
|
|
Adding `:no-doc` route data to exclude the first routes from generated [Swagger documentation](../ring/swagger.md):
|
|
|
|
```clj
|
|
[["" {:no-doc true}
|
|
["/swagger.json" ::swagger]
|
|
["/api-docs" ::api-docs]]
|
|
["/api/ping" ::ping]
|
|
["/api/pong" ::pong]]
|
|
```
|
|
|
|
Accumulated route data:
|
|
|
|
```clj
|
|
(def router
|
|
(r/router
|
|
[["" {:no-doc true}
|
|
["/swagger.json" ::swagger]
|
|
["/api-docs" ::api-docs]]
|
|
["/api/ping" ::ping]
|
|
["/api/pong" ::pong]]))
|
|
|
|
(r/routes router)
|
|
; [["/swagger.json" {:no-doc true, :name ::swagger}]
|
|
; ["/api-docs" {:no-doc true, :name ::api-docs}]
|
|
; ["/api/ping" {:name ::ping}]
|
|
; ["/api/pong" {:name ::pong}]]
|
|
```
|
|
|
|
## Top-level Route Data
|
|
|
|
Route data can be introduced also via `Router` option `:data`:
|
|
|
|
```clj
|
|
(def router
|
|
(r/router
|
|
["/api"
|
|
{:middleware [::api]}
|
|
["/ping" ::ping]
|
|
["/pong" ::pong]]
|
|
{:data {:middleware [::session]}}))
|
|
```
|
|
|
|
Expanded routes:
|
|
|
|
```clj
|
|
[["/api/ping" {:middleware [::session ::api], :name ::ping}]
|
|
["/api/pong" {:middleware [::session ::api], :name ::pong}]]
|
|
```
|
|
|
|
|
|
## Customizing Expansion
|
|
|
|
By default, router `:expand` option has value `r/expand` function, backed by a `r/Expand` protocol. Expansion can be customized either by swapping the `:expand` implementation or by extending the Protocol. `r/Expand` implementations can be recursive.
|
|
|
|
Naive example to add direct support for `java.io.File` route argument:
|
|
|
|
```clj
|
|
(extend-type java.io.File
|
|
r/Expand
|
|
(expand [file options]
|
|
(r/expand
|
|
#(slurp file)
|
|
options)))
|
|
|
|
(r/router
|
|
["/" (java.io.File. "index.html")])
|
|
```
|
|
|
|
Page [shared routes](../advanced/shared_routes.md#using-custom-expander) has an example of an custom `:expand` implementation.
|
|
|
|
## Route data validation
|
|
|
|
See [Route data validation](route_data_validation.md).
|