Creating a Form

Forms provide a convenient way of getting input from your users. This section looks at the tools provided by CL-HTTP for constructing forms, and processing the data received back from forms.

To create a computed form you use the export-url function with the parameter :html-computed-form. It takes two Lisp routines:

  • The :form-function, which is returned when you connect to the URL specified for the form page.

  • The :response-function, which receives the data when the user clicks the Submit button to post the form values back to the server.

In this first example we'll create a computed form to implement a simple bulletin board. Users can add comments to the board by typing them into a text box and clicking the Submit button.

Full listing


The bulletin board will be stored in this global variable:

(defparameter *board* nil)

First let's define the routine to display the board:

(defun display-bulletin-board (url stream)
  "Routine to display a bulletin board and a form."
  (with-page (url stream "Feedback")
             (dolist (topic (reverse *board*))
               (with-paragraph (:stream stream) 
                 (write-string-quoting-specials topic stream)))
             (with-fillout-form (:post url :stream stream)
               (accept-input 'string "text" :stream stream)
               (accept-input 'submit-button "Submit" :stream stream))))

This first displays a list of topic strings stored in the variable *board*. This is initially empty. Note that the strings are written to the stream by the CL-HTTP routine write-string-quoting-specials to ensure that if the string entered by the user contains any of the special characters ampersand, less-than, greater-than, or double-quote, these will be escaped.

The routine with-fillout-form creates the form. It takes two parameters: the type of response, usually :post, and the URL of the response function. Usually, as in this case, the form processing is handled by the same URL as the form function, so we can just give the second parameter as the url of the page.

The calls to accept-input create the various form elements - in this case a text input box, and a Submit button.

The response function

When the user clicks the Submit button the form values are posted to the response function update-bulletin-board:

(defun update-bulletin-board (url stream alist)
  "Response function to add an entry to the bulletin board."
  (bind-query-values (text) (url alist)
    (atomic-push text *board*)
      (display-bulletin-board url stream)))

The CL-HTTP routine bind-query-values allocates each of the form values to a variable with the name of the form field. In this case text is bound to the text the user typed into the text field. The response function pushes this on the *board* list, using the CL-HTTP function atomic-push as different users may be updating the board simultaneously. It then calls display-board again to display the new list of topics, and a new form.

Finally, here's the call to export the URL to the CL-HTTP server:

(export-url "http://localhost:8000/board.html"
            :form-function 'display-bulletin-board
            :response-function 'update-bulletin-board)

Going to the URL /board.html displays the following page:


To add an entry to the board you enter some text and click Submit:


The topic is added to the list of topics, and the form is redisplayed with the new topic:


blog comments powered by Disqus