(:html
(:head
(:title 'Random numbers'))
(:body
(:h1 'Random numbers')
(:p (loop repeat 10 do (html (:print (random 1000)) ' '))))))
The macro version will be quite a bit more efficient than the emit-html
version. Not only do you never have to generate an s-expression representing the whole page, also much of the work that emit-html
does at runtime to interpret the s-expression will be done once, when the macro is expanded, rather than every time the code is run.
You can control where the output generated by both html
and emit-html
is sent with the macro with-html-output
, which is part of the FOO library. Thus, you can use the with-html-output
and html
macros from FOO to rewrite random- number
like this:
(defun random-number (request entity)
(with-http-response (request entity :content-type 'text/html')
(with-http-body (request entity)
(with-html-output ((request-reply-stream request))
(html
(:html
(:head (:title 'Random'))
(:body
(:p 'Random number: ' (:print (random 1000))))))))))
Another feature of FOO is that it allows you to define HTML 'macros' that can translate arbitrary forms into HTML s-expressions that the html
macro understands. For instance, suppose you frequently find yourself writing pages of this form:
(:html
(:head (:title 'Some title'))
(:body
(:h1 'Some title')
You could define an HTML macro to capture that pattern like this:
(define-html-macro :standard-page ((&key title) &body body)
`(:html
(:head (:title ,title))
(:body
(:h1 ,title)
,@body)))
Now you can use the 'tag' :standard-page
in your s-expression HTML, and it'll be expanded before being interpreted or compiled. For instance, the following:
(html (:standard-page (:title 'Hello') (:p 'Hello, world.')))
generates the following HTML:
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello</h1>
<p>Hello, world.</p>
</body>
</html>
Of course, generating HTML output is only half of Web programming. The other thing you need to do is get input from the user. As I discussed in the 'A 30-Second Intro to Server-Side Web Programming' section, when a browser requests a page from a Web server, it can send query parameters in the URL and post data, both of which act as input to the server-side code.
AllegroServe, like most Web programming frameworks, takes care of parsing both these sources of input for you. By the time your published functions are called, all the key/value pairs from the query string and/or post data have been decoded and placed into an alist that you can retrieve from the request object with the function request-query
. The following function returns a page showing all the query parameters it receives:
(defun show-query-params (request entity)
(with-http-response (request entity :content-type 'text/html')
(with-http-body (request entity)
(with-html-output ((request-reply-stream request))
(html
(:standard-page
(:title 'Query Parameters')
(if (request-query request)
(html
(:table :border 1
(loop for (k . v) in (request-query request)
do (html (:tr (:td k) (:td v))))))
(html (:p 'No query parameters.')))))))))
(publish :path '/show-query-params' :function 'show-query-params)
If you give your browser a URL with a query string in it like the following:
http://localhost:2001/show-query-params?foo=bar&baz=10
you should get back a page similar to the one shown in Figure 26-4.

To generate some post data, you need an HTML form. The following function generates a simple form, which submits its data to show-query-params
:
(defun simple-form (request entity)
(with-http-response (request entity :content-type 'text/html')
(with-http-body (request entity)
(let ((*html-output* (request-reply-stream request)))
(html
(:html