Routing
Previously the recommendate way for processing different URL
paths was use of reblocks-navigation-widget
system.
But now Reblocks integrates with 40ants-routes
and it is possible to specify which widgets to show for the URL
path.
To do this, use ROUTES
argument of DEFAPP
macro, like this:
(defapp tasks
:prefix "/"
:routes ((page ("/<int:task-id>" :name "task-details")
(make-task-page task-id))
(page ("/" :name "tasks-list")
(make-task-list "First"
"Second"
"Third"))))
In this example we define application with two routes. Route /
is served by rendering list of tasks.
Route like /100500
is served by rendering task-page
widgets.
How does Routing Work
In reblocks, when you start a server, the server holds a root routes collection.
Routes of each application included into the server are included into this root routes collection using a prefix, specified
for the DEFAPP
form.
For example, you can define three apps like this:
(defapp site
:prefix "/"
:routes ((page ("/" :name "landing")
(make-landing-page))))
(defapp blog
:prefix "/blog/"
:routes ((page ("/<int:post-id>" :name "post")
(make-post-page post-id))))
(defapp admin
:prefix "/admin/"
:routes ((page ("/" :name "dashboard")
(make-dashboard-page))
(page ("/users/" :name "users")
(make-users-page))
(page ("/users/<int:user-id>" :name "user")
(make-user-page user-id))
(page ("/posts/" :name "posts")
(make-posts-page))
(page ("/posts/<int:post-id>" :name "post")
(make-post-page user-id))))
then server will combine their routes into the routes hierarchy.
Somebody could also create a library which returns 40ants-routes/routes:routes
class instance. For example,
such library might provide admin views to control some class of entities. In this case abover
code could be rewritten like this:
(defapp admin
:prefix "/admin/"
:routes ((page ("/" :name "dashboard")
(make-dashboard-page))
(include (crud 'user)
:path \"users\")
(include (crud 'post)
:path \"posts\")))
Here we've used crud
function which returns a 40ants-routes/routes:routes
class collection to show the list
of entities of given type and allow to create, edit, delete them.
Wrapping the Page
Often you want to render the same header and footer around each page. In this case, you might use
argument PAGE-CONSTRUCTOR
of the DEFAPP
macro. If specified, PAGE-CONSTRUCTOR
argument should be
a function of one argument - widget returned by a route's handler. In the most simple form,
such page constructor just creates a widget which wraps page content:
(defun wrap-with-frame (widget)
(make-instance 'page-frame-widget
:content widget))
Serving the Static
Another case when you might want to define a route is serving static file.
The most common case is serving robots.txt
and favicon.ico
. In such cases,
your route handler should return a list instead of widget:
(defapp app
:prefix "/"
:routes ((page ("/" :name "index")
(make-landing-page))
;; First way of serving the favicon:
(static-file "/favicon.ico"
(asdf:system-relative-pathname :my-app "favicon.ico")
:content-type "image/x-icon")
;; Alternative would be:
(get ("/favicon.ico")
(list 200
(list :content-type "image/x-icon")
(asdf:system-relative-pathname :my-app
"favicon.ico")))
;; Also we can return just a string and it will become
;; a page's content:
(get ("/robots.txt")
"User-agent: *")))
Here we see a two alternative ways to serve a favicon file:
The first way is to use
reblocks/routes:static-file
function for constructing the route.The second way is more generic - it uses
40ants-routes/defroutes:get
with the body returning a list in the format defined by Clack web server.
As you can see, when you are using 40ants-routes/defroutes:get
, the route's handler might
return a list of three items: http code, http headers and a pathname. Or just a string.
Actually, this reponse is passed to the Clack as is and anything supported by Clack is supported.