Archives

Creative Commons License
This blog is licensed under a Creative Commons License.

A quick Hunchentoot primer

| No Comments | No TrackBacks

I wrote yesterday about setting up Hunchentoot, a Common Lisp web server running behind Apache, for rendering dynamic web pages in Lisp. What I neglected to mention was how one goes about coding such pages. Fortunately, that’s the easiest part of all, so I wanted to provide a very short primer on getting your first Lisp web pages up and running.

I’m going to assume that you’ve implemented every step in yesterday’s article, including the Swank server that allows you to connect to a running Hunchentoot server over SSH, using Emacs and SLIME. This means you’ve already opened your SSH tunnel to the server, typed M-x slime-connect, bound it to your local reflection port, and now have a Lisp REPL talking to your Hunchentoot server. See yesterday’s article if you still don’t have that running yet. Or contact me if you’re having a difficult time getting things to work.

With that setup, create a file in Emacs called webserver.lisp. If you don’t have SLIME setup locally, try using Lisp in a Box if you’re on Windows or Linux, or Ready Lisp if you’re on Mac OS X. You’ll know it’s all working if you see “Slime:” in your buffer’s mode-line, indicating the Lisp buffer is associated with your remote SLIME connection.

Now enter the following forms into your Lisp file. You could enter these into the Lisp REPL directly, but writing them in a file first is a handy way of taking notes and preserving your work in a textual form:

(defpackage :webserver
  (:use :common-lisp :hunchentoot :cl-who))

(in-package :webserver)

(setf *dispatch-table*
      (list #'dispatch-easy-handlers
            #'default-dispatcher))

(setf *show-lisp-errors-p* t
      *show-lisp-backtraces-p* t)

This sets up the basic framework for your webserver. Once saved to disk, type C-x h to mark the entire buffer, and C-c C-r to submit the region to the remote Lisp for evaluation. (Note: The second setq above is completely optional, and should be removed for a production server. But having it makes errors much easier to track down in the initial stages).

Your basic dispatcher is now setup. If you visit the home page of your webserver, however, you’ll still see the old default page because you’re using default-dispatcher for everything. Let’s define a new page handler that does some work.

Below is a basic Hello World handler. Add it to the end of your file, put your cursor anywhere within the handler’s definition, and type C-c C-c to have SLIME compile the definition into your webserver:

(define-easy-handler (easy-demo :uri "/lisp/hello"
                                :default-request-type :get)
    ((state-variable :parameter-type 'string))
  (with-html-output-to-string (*standard-output* nil :prologue t)
    (:html
     (:head (:title "Hello, world!"))
     (:body
      (:h1 "Hello, world!")
      (:p "This is my Lisp web server, running on Hunchentoot,"
          " as described in "
          (:a :href
              "http://newartisans.com/blog_files/hunchentoot.primer.php"
              "this blog entry")
          " on Common Lisp and Hunchentoot.")))))

This kind of easy handler is just that: very easy to make. Hunchentoot also allows for much more sophisticated handlers, about which I refer you to the documentation. But right now we just need a handler to capture requests bound for /lisp/hello, in response to which we’ll return a Lisp-constructed HTML page. The reason I using the path /lisp/hello here has to do with the way my Apache is configured. If you run Hunchentoot at root level, you could use /hello instead.

Try visiting your /lisp/hello page now. You should see a brief greeting to the entire planet!

You may have noticed, after compiling this definition, that it warns about state-variable not being used. There’s a good reason for that: we never used it. So let’s further extend the example server with an input textbox and submit button, using the state variable to get information back from the client via HTTP POST. Notice how the :default-request-type changes to :post in this code:

(define-easy-handler (easy-demo :uri "/lisp/hello"
                                :default-request-type :post)
    ((state-variable :parameter-type 'string))
  (with-html-output-to-string (*standard-output* nil :prologue t)
    (:html
     (:head (:title "Hello, world!"))
     (:body
      :style "margin: 20px"
      (:h1 "Hello, world!")
      (:p "This is my Lisp web server, running on Hunchentoot,"
          " as described in "
          (:a :href
              "http://newartisans.com/blog_files/hunchentoot.primer.php"
              "this blog entry")
          " on Common Lisp and Hunchentoot.")
      (:p (:form
           :method :post
           (:table
            :border 0 :cellpadding 5 :cellspacing 0
            (:tr (:td :style "text-align: right" (str "Say hello:"))
                 (:td (:input :type :text
                                    :name "state-variable"
                                    :value state-variable))
                 (:td (:input :type :submit :value "Submit"))))))
      (:p "The string you entered was: " (str state-variable))))))

If you visit my own Hunchentoot server right now, this is exactly the page you’ll see, handled by this very code (which I submitted to the server via Emacs, without ever restarting it):

hunchentoot-default-page.png

And that’s that, simple web pages rendered by the multi-threaded Hunchentoot application server. Anything not on the /lisp page is served by Apache, while everything under “/lisp” goes to Hunchentoot. But again, this depends entirely on how you have Apache configured.

At this point I recommend visiting the Hunchentoot and CL-WHO web pages, to learn more about what these frameworks have to offer.

For those from an ASP or PHP background: because Lisp already provides such good facilities for constructing and manipulating hierarchical data, you’ll never find yourself inserting code stubs directly into HTML code – or dealing with the kind of hacks other frameworks use that turn plain HTML into a pseudo-language for rendering lists of controls, etc. When you work in Lisp, everything is in Lisp.

No TrackBacks

TrackBack URL: http://www.newartisans.com/mt/mt-tb.cgi/28

Leave a comment

About this Entry

This page contains a single entry by John Wiegley published on November 6, 2007 4:51 PM.

Running Common Lisp behind Apache was the previous entry in this blog.

Ready Lisp for OS X Leopard is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Recent Comments

  • Przemek: Great article, indeed. @Kyle: I’m a git newbie and SVN read more
  • Kyle Bennett: John, thanks for the work you put into this. Since read more
  • Sigi: You deserve a lot of praise for this article. It’s read more
  • Tony: this is an excellent write up; I’ve been reading much read more
  • John Wiegley: Thanks for letting me know, I’ll try to rectify the read more
  • Rudi Farkas: Hello John Above, you say “The date at the front read more
  • Uwe Kleine-König: Hi John, a comment to the paragraph about reset: $ read more
  • Leonardo Boiko: Thank you very much for this; as a bottom-up guy read more
  • John Wiegley: Thanks for the update. I’ll include this among the next read more
  • Laust Rud: Excellent writing, thanks! The url for the git-core tutorial has read more
OpenID accepted here Learn more about OpenID
Powered by Movable Type 4.25