the first URL parameter
As of now we can access URLs like these: http://localhost:8899/product/0 where 0 is a path parameter.
We will soon need URL parameters so let’s add one to our routes.
We want a debug
URL parameter that will show us more data. The URL
will accept a ?debug=t
part.
We have this product route:
(easy-routes:defroute product-route ("/product/:n") (&path (n 'integer))
(render *template-product* :product (get-product n)))
With or without the &path
part, either is good for now, so let’s remove it for clarity:
(easy-routes:defroute product-route ("/product/:n") ()
(render *template-product* :product (get-product n)))
We removed it because it’s as the same place that we will define URL parameters. Let’s add one.
GET and POST parameters
Our route with a debug
URL parameter becomes:
(easy-routes:defroute product-route ("/product/:n") (&get debug)
(render *template-product* :product (get-product n)))
The &get
is here to say this parameter is accepted only in GET
requests. You can have &post
, and you can leave aside the &get
or
&post
for parameters that are always accepted.
Let’s not parse the value of the debug
variable: if it’s present,
it’s truthy, and we should display debug information.
Let’s add logic to the template.
The if
tag of Djula is of the form: {% if %} … {% else %} … {% endif %}
.
So:
{% if debug %} debug info! {% endif %}
And pass the variable to the render
function:
(render *template-product*
:product (get-product n)
:debug debug)
Go to http://localhost:8899/product/0?debug=t and you should see
(0 Product nb 0 9.99) debug info!
Can we do something useful? In my apps, printing debug info and the
output of describe
for some objects turned useful (grab this output
with with-output-to-string
).
Full code
Our app looks like this:
(in-package :myproject)
(defvar *server* nil
"Server instance (Hunchentoot acceptor).")
(defparameter *port* 8899 "The application port.")
(defparameter *template-root* "
<title> Lisp web app </title>
<body>
<ul>
{% for product in products %}
<li>
<a href=\"/product/{{ product.0 }}\">{{ product.1 }} - {{ product.2 }}</a>
</li>
{% endfor %}
</ul>
</body>
")
(defparameter *template-product* "
<body>
{{ product }}
{% if debug %} debug info! {% endif %}
</body>
")
(defun get-product (n)
(list n (format nil "Product nb ~a" n) 9.99))
(defun products (&optional (n 5))
(loop for i from 0 below n
collect (get-product i)))
(defun render (template &rest args)
(apply
#'djula:render-template*
(djula:compile-string template)
nil
args))
(easy-routes:defroute root ("/") ()
(render *template-root* :products (products)))
(easy-routes:defroute product-route ("/product/:n") (&get debug &path (n 'integer))
(render *template-product*
:product (get-product n)
:debug debug))
(defun start-server (&key (port *port*))
(format t "~&Starting the web server on port ~a" port)
(force-output)
(setf *server* (make-instance 'easy-routes:easy-routes-acceptor
:port port))
(hunchentoot:start *server*))
Everything is contained in one file, and we can run everything from sources or we can build a self-contained binary. Pretty cool!
Before we do so, we’ll add a great feature: searching for products.