Fork me on GitHub

RESTAS

Framework for web application development in Common Lisp

Hello World

A simple example

Here is a classic "Hello world":

(asdf:operate 'asdf:load-op '#:restas)

(restas:define-module #:restas.hello-world
  (:use :cl)
)


(in-package #:restas.hello-world)

(restas:define-route main ("")
  "<h1>Hello world!</h1>"
)


(restas:start '#:restas.hello-world :port 8080)

If you execute this code and open http://localhost:8080/ in your browser, you will see "Hello world" heading. That's nice. :)

Now, one after another, line by line:

To begin with, lets load RESTAS itself:

(asdf:operate 'asdf:load-op '#:restas)

Then we define a new module:

(restas:define-module #:restas.hello-world
  (:use :cl)
)

Actually, this code only creates a package and initializes it, adding several variables to it. A module is not an object or a symbol, but is associated with a package. Why is it done in a such way? Using a package as a module lets us, in most cases, not to specify the module explicitly, but just to place the module's code into the package associated with it.

Now we change current package.

(in-package #:restas.hello-world)

Then we create a route for processing GET requests to the site root.

(restas:define-route main ("")
  "<h1>Hello world!</h1>"
)

Inside ot the macro restas:define-route you can place any code, which must return a string, or an octets array, or a pathname (in this case client will get according file), or an integer number (which is interpreted as request status, for example, hunchentoot:+http-not-found++). Also, this code can freely use any variables, noted in the Hunchentoot web server's documentation (e.g. hunchentoot:*request* or hunchentoot:*reply*).

Lets launch the web server and activate the site:

(restas:start '#:restas.hello-world :port 8080)

Processing of POST requests.

This example shows different treatment of GET and POST requests:

(asdf:operate 'asdf:load-op :cl-who)
(asdf:operate 'asdf:load-op :restas)

(restas:define-module :restas.example-1
  (:use :cl)
)


(in-package :restas.example-1)

(restas:define-route main ("" :method :get)
  (who:with-html-output-to-string (out)
    (:html
     (:body
      ((:form :method :post)
       ((:input :name "message"))
       ((:input :type "submit" :value "Send"))
)
)
)
)
)


(restas:define-route main/post ("" :method :post)
  (who:with-html-output-to-string (out)
    (:html
     (:body
      (:div
       (:b (who:fmt "test message: ~A"
                    (hunchentoot:post-parameter "message")
)
)
)

      ((:a :href (restas:genurl 'main)) "Try again")
)
)
)
)


(restas:start '#:restas.example-1 :port 8080)

When you execute it and open the page http://localhost:8080/, you will see an elementary form, in which you are offered to type an arbitrary message. After that you can Send it and get a new page, containing a typed message and a link with an offer to repeat your input. Just a little bit to improve and you can start selling it. :)

This example is different from previous in that it now contains two routes, one for processing GET requests and the other one for POST ones. As you see, you can specify the type of request the route concerns, with key :method, which is :get by default. Presence of "post" ending in 'main/post route name is just a coincidence: routes can be named in an arbitrary way, as long as each route has a unique name.

Parameterized routes

The URL template specified in restas:define-route isn't required to be as simple as in previous examples. It can contain several parameters available for the handler and specified inside of restas:define-route.

(asdf:operate 'asdf:load-op :cl-who)
(asdf:operate 'asdf:load-op :restas)

(restas:define-module :restas.example-2
  (:use :cl :iter)
)


(in-package :restas.example-2)

(restas:define-route root ("")
  (who:with-html-output-to-string (out)
    (:html
     (:head
      (:title "Example 2: Index")
)

     (:body
      (:h1 "Index")
      (:ul
       (iter (for x from 1 to 10)
             (who:htm (:li
                       ((:a :href (restas:genurl 'chapter-?.html :id x))
                        (who:fmt "Chapter ~A" x)
)
)
)
)
)
)
)
)
)


(restas:define-route chapter-?.html ("chapter-:(id).html")
  (who:with-html-output-to-string (out)
    (:html
     (:head
      (:title (who:fmt "Example 2. Chapter ~A" id))
)

     (:body
      (:h1 (who:fmt "Chapter ~A" id))
      (:ul
       (iter (for x from 1 to 10)
             (who:htm (:li
                       ((:a :href (restas:genurl 'chapter-?-?.html :id1 id :id2 x))
                        (who:fmt "Chapter ~A-~A" id x)
)
)
)
)
)

      ((:a :href (restas:genurl 'root))
       "Back to Index"
)
)
)
)
)


(restas:define-route chapter-?-?.html ("chapter-:(id1)-:(id2).html")
  (who:with-html-output-to-string (out)
    (:html
     (:head
      (:title (who:fmt "Example 2. Chapter ~A-~A" id1 id2))
)

     (:body
      (:h1 (who:fmt "Chapter ~A-~A" id1 id2))
      (:p (who:fmt "This is a chapter ~A-~A" id1 id2))
      ((:a :href (restas:genurl 'chapter-?.html :id id1))
       (who:fmt "Back to Chapter ~A" id1)
)
)
)
)
)


(restas:start :restas.example-2 :port 8080)

This example demonstrates navigation in some "Book with table of contents", parametrized routes, and generation of links from names of routes.

The code of the above examples comes with RESTAS in the 'example' directory.

@2009-2011 Moskvitin Andrey