Generating HTML

In the Demo Page example we used a series of format statements to emit the HTML needed by our page.

In fact CL-HTTP provides a much more flexible solution. It includes routines to emit all the HTML you need to construct finished Web pages. Using these routines has several advantages over printing the HTML directly:

  • The CL-HTTP routines are generally descriptive of their function, so you can compose Web pages without knowing HTML.
  • They take care of nesting tags and end tags correctly.
  • Where possible, they check the validity of the parameters.
  • The same routines can be used to generate different types of markup; for example, XHTML.

Here are some examples:

Routine Description
with-html-document Creates a <html> ... </html>  block.
with-document-preamble Creates a <head> ... </head> block.
declare-title Creates a <title> ... </title> statement.
with-document-body Creates a <body> ... </body> block.
with-section-heading Creates a <h1> ... </h1> heading.
with-paragraph Creates a <p> ... </p> paragraph.

The with- routines enclose a block of statements with a start tag and end tag, whereas the declare routines emit a single construct. Each routine takes a :stream keyword parameter to specify the output stream used by the Web server.

The Demo Page example rewritten to use these HTML generating routines is then as follows:

(defun write-demo-page2 (url stream)
  "The response function for a simple computed URL, using HTML generation."
  (declare (ignore url))
  (with-successful-response (stream :html)
    (with-html-document (:stream stream)
      (with-document-preamble (:stream stream)
        (declare-title "Demo Page" :stream stream))
      (with-document-body (:stream stream)
        (with-section-heading ("Demo Page" :stream stream)
          (with-paragraph (:stream stream)
            (format stream "Welcome to our site!")))))))

(export-url "http://localhost:8000/demo2.html"
            :response-function 'write-demo-page2)

Creating a standard response function

For convenience we can wrap the generation of the head and body codes in a with-page macro. It takes as parameters the url, stream, and page title:

(defmacro with-page ((url stream title) &body body)
  "Provides the response function to emit a page body."
  `(with-successful-response (,stream :html)
     (with-html-document (:stream ,stream)
       (with-document-preamble (:stream ,stream)
         (declare-title ,title :stream ,stream))
       (with-document-body (:stream ,stream)
         (with-section-heading (,title :stream ,stream)

Our demo page can then be written as:

(defun write-demo-page3 (url stream)
  (declare (ignore url))
  (with-page (url stream "Demo Page")
    (with-paragraph (:stream stream)
      (format stream "Welcome to our site!"))))

This is exported with:

(export-url "http://localhost:8000/demo3.html"
            :response-function 'write-demo-page3)

We will use the with-page macro in the remaining examples in this tutorial.

blog comments powered by Disqus