From: Zachary Beane
Subject: Web applications and conditions
Date: 
Message-ID: <slrnbkk83g.k5m.xach@unnamed.xach.com>
I'm a Lisp novice, working on one of my first web applications. I'd
like to compose the program such that conditions indicate when there's
an issue that needs correcting, and it's possible to do recovery
without intermixing it with normal program flow.

For example, if I'm serving a page that requires a user to be logged
in, I'd like to signal a condition if the page is accessed by a
non-logged-in user, go to the login process, and return the results of
the original request when the login process is done.

Another situation might be that a form is submitted with errors; I'd
like to collect the errors, redisplay the form with erroroneous
sections flagged, and continue to the next step when the errors have
all been corrected (which may take a few iterations).

However, I'm running into trouble mapping this idea to the way HTTP
works. Here's something that doesn't work, in fake code:

   (defun get-user (request)
     (if (logged-in-p request)
         (request-user request)
         (signal-a-"user not logged in"-condition)))

   (defun user-info-page (request)
     (let ((user (get-user request)))
       (send-to-browser "Your name is ~A" (user-name user))))

   (serve-web-page "/your-info"
                   #'(lambda (request)
                       (handler-case (user-info-page request)
                         ("user not logged in" (c) (log-user-in c)))))

Any ideas on how to achieve this in the disconnected HTTP
client/server model? Or a better way to arrange for the handling of
situations like this?

Zach

From: Jock Cooper
Subject: Re: Web applications and conditions
Date: 
Message-ID: <m3r839fwqb.fsf@jcooper02.sagepub.com>
Zachary Beane <····@xach.com> writes:

> I'm a Lisp novice, working on one of my first web applications. I'd
> like to compose the program such that conditions indicate when there's
> an issue that needs correcting, and it's possible to do recovery
> without intermixing it with normal program flow.
> 
> For example, if I'm serving a page that requires a user to be logged
> in, I'd like to signal a condition if the page is accessed by a
> non-logged-in user, go to the login process, and return the results of
> the original request when the login process is done.
> 
> Another situation might be that a form is submitted with errors; I'd
> like to collect the errors, redisplay the form with erroroneous
> sections flagged, and continue to the next step when the errors have
> all been corrected (which may take a few iterations).
> 
> However, I'm running into trouble mapping this idea to the way HTTP
> works. Here's something that doesn't work, in fake code:
> 
> Any ideas on how to achieve this in the disconnected HTTP
> client/server model? Or a better way to arrange for the handling of
> situations like this?
> 

Using Allegroserve the code for user validation and handling program 
errors is quite simple, it looks something like this:

(defun serve-page (req ent)
"serve-page is the #'publish'ed function"
  (multiple-value-bind (user pw) (get-basic-authorization req)
    (... validate user and pw ...)
   (if (..bad or no user..)
     ; this code pops a login dialog
     (with-http-response (req ent :response *response-unauthorized*) 
        (set-basic-authorization req "My app name")
        (with-http-body (req ent))))  ; empty page; browser will resubmit
     ; user validated
     (with-http-response (req ent) 
       (restart-case 
          (handler-case 
              (...page processing/generating code here...)
           ; catch errors below
           (dbi:odbc-error (cond)
             (with-http-body (req ent)
               ...write the error info into the return page...)
             (invoke-restart 'prep-sess-err-restart nil nil nil))
           ..catch other 'specific' errors..
           (error (cond)                 ; catch all other errors
             (with-http-body (req ent)
               ...write general error..)
             (invoke-restart 'prep-sess-err-restart nil nil nil)))
         (prep-sess-err-restart (&optional v1 v2 v3)) 
                (values v1 v2 v3))))))

As far as catching form/page errors (eg field requires value etc),
you can check that server side or client side using javascript.
I do it in javascript;  when my Lisp outputs the <form>..</form>
stuff I also output javascript to do validation, manage multiple
submit buttons, validate control groups etc. 
Server side validation works too, you just have to validate the page
in "...page generating code here...", then send the same page back
if there was an error.
Or you could signal a "form error" condition in that code; and
catch it in the handler case.  I prefer to just reemit the form;
otherwise you have to pass a lot of info about the form and the
errors into the error handler.


Jock Cooper
--
http://www.fractal-recursions.com
From: Zachary Beane
Subject: Re: Web applications and conditions
Date: 
Message-ID: <slrnbkkudk.k5m.xach@unnamed.xach.com>
In article <··············@jcooper02.sagepub.com>, Jock Cooper wrote:
> Zachary Beane <····@xach.com> writes:
> 
>> I'm a Lisp novice, working on one of my first web applications. I'd
>> like to compose the program such that conditions indicate when there's
>> an issue that needs correcting, and it's possible to do recovery
>> without intermixing it with normal program flow.
>> 
>> For example, if I'm serving a page that requires a user to be logged
>> in, I'd like to signal a condition if the page is accessed by a
>> non-logged-in user, go to the login process, and return the results of
>> the original request when the login process is done.
>> 
>> Another situation might be that a form is submitted with errors; I'd
>> like to collect the errors, redisplay the form with erroroneous
>> sections flagged, and continue to the next step when the errors have
>> all been corrected (which may take a few iterations).
>> 
>> However, I'm running into trouble mapping this idea to the way HTTP
>> works. Here's something that doesn't work, in fake code:
>> 
>> Any ideas on how to achieve this in the disconnected HTTP
>> client/server model? Or a better way to arrange for the handling of
>> situations like this?
>> 
> 
> Using Allegroserve the code for user validation and handling program 
> errors is quite simple, it looks something like this:
> 
> (defun serve-page (req ent)
> "serve-page is the #'publish'ed function"
>   (multiple-value-bind (user pw) (get-basic-authorization req)
>     (... validate user and pw ...)
>    (if (..bad or no user..)
>      ; this code pops a login dialog
>      (with-http-response (req ent :response *response-unauthorized*) 
>         (set-basic-authorization req "My app name")
>         (with-http-body (req ent))))  ; empty page; browser will resubmit
[snip]

This doesn't cut it. I *don't* want to have to put in code that logs
in a user into the function that serves a page that requires a
logged-in user. I'd like to assume that the function for obtaining
user information will do whatever is necessary to get me valid
information.

That is, as I understand it, part of the motivation for the condition
system: you can write code that deals with the normal situation, and
raise conditions matched to handlers to deal with other
situations. You don't have to write in an excessively defensive "did
this work? did this work? did this work?" style.

Unfortunately, I can't see how to use conditions in the HTTP
communication model.

> As far as catching form/page errors (eg field requires value etc),
> you can check that server side or client side using javascript.
> I do it in javascript;  when my Lisp outputs the <form>..</form>
> stuff I also output javascript to do validation, manage multiple
> submit buttons, validate control groups etc. 

I don't really consider client-side validation an option, since I
don't have any assurances that the client will actually do it. As a
result, the server has to validate the input anyway, and I'd rather
not maintain parallel Lisp and JavaScript validation code.

Zach
From: Tim Lavoie
Subject: Re: Web applications and conditions
Date: 
Message-ID: <87oeyd73gn.fsf@theasylum.dyndns.org>
Hi Zach,

I can't answer the Lisp-specific bits of your query, but maybe I can
make some sense of the web-app itself. My understanding of what you
what is that you want to keep the pages themselves fairly simple, but
have the application do the right thing when something comes up.

One system which does this sort of thing is the Struts project at
apache.org. I know, it's Java, but go with me here. The system is
essentially a higher-level MVC framework, so the pages themselves are
just the view component with separate model and controller parts. For
a given action, you have defined what to do when something goes wrong,
such as missing required elements in a form submission. The state
info, such as the pile of parameters which were submitted, is passed
along and used to pre-fill form fields. When complete, it all goes to
where it should have the first time.

You probably don't want anything nearly so complex (Struts is big),
but have a look at it for some ideas of what you would like. I suspect
that the condition system would work quite well in implementing the
framework, rather than being something to code in at the page level.

So, for a given action, you would capture the various form fields and
query string, then check for authentication and whatever logic you
like. Each action will have an error or "fix this" destination, filled
with the state info and your error message; if there is still a
problem, you return to the same error page. Once fixed, pass the whole
works to that action's registered success destination as if nothing
had occurred.

It should be noted that while Struts supports a large template
library, the templated pages are just for display output, not business
logic. That helps immensely if you do anything large, because UI
changes are separated from the core app logic. Some systems stuff
everything into JSP templates, which combines the worst of both.

  Cheers,
  Tim


-- 
"Golf is a game whose sole aim is to hit a very small ball into an
even smaller hole, with weapons singularly ill designed for that
purpose."
    -- Winston Churchill
From: Edi Weitz
Subject: Re: Web applications and conditions
Date: 
Message-ID: <87u1858lkm.fsf@bird.agharta.de>
On Mon, 25 Aug 2003 15:59:00 -0500, Zachary Beane <····@xach.com> wrote:

> This doesn't cut it. I *don't* want to have to put in code that logs
> in a user into the function that serves a page that requires a
> logged-in user. I'd like to assume that the function for obtaining
> user information will do whatever is necessary to get me valid
> information.
> 
> That is, as I understand it, part of the motivation for the
> condition system: you can write code that deals with the normal
> situation, and raise conditions matched to handlers to deal with
> other situations. You don't have to write in an excessively
> defensive "did this work? did this work? did this work?" style.
> 
> Unfortunately, I can't see how to use conditions in the HTTP
> communication model.

I think this depends on how your (Lisp) web server code generally
handles requests. If you generate the whole output first (i.e. write
it into a string) and only afterwards send it to the browser (which is
the preferred way to work with mod_lisp) you can at any time abort the
page generating code and transfer control to the main loop which can
have a handler to, say, redirect the client to an error page.

For an example see the description at

  <http://article.gmane.org/gmane.lisp.web/153>

and in particular the part about using THROW to send a response.

FWIW, in a large project of one of my clients which is written in
(sigh...) PHP we use a similar approach, although not as elegant as
would be possible with Lisp: We use PHP's auto-prepend/-append feature
to automatically buffer the HTML output and install a custom error
handler which tries to do something meaningful if an error occurs.

Edi.
From: Zachary Beane
Subject: Re: Web applications and conditions
Date: 
Message-ID: <slrnbkl1ia.k5m.xach@unnamed.xach.com>
In article <··············@bird.agharta.de>, Edi Weitz wrote:
> On Mon, 25 Aug 2003 15:59:00 -0500, Zachary Beane <····@xach.com> wrote:
> 
>> This doesn't cut it. I *don't* want to have to put in code that logs
>> in a user into the function that serves a page that requires a
>> logged-in user. I'd like to assume that the function for obtaining
>> user information will do whatever is necessary to get me valid
>> information.
>> 
>> That is, as I understand it, part of the motivation for the
>> condition system: you can write code that deals with the normal
>> situation, and raise conditions matched to handlers to deal with
>> other situations. You don't have to write in an excessively
>> defensive "did this work? did this work? did this work?" style.
>> 
>> Unfortunately, I can't see how to use conditions in the HTTP
>> communication model.
> 
> I think this depends on how your (Lisp) web server code generally
> handles requests. If you generate the whole output first (i.e. write
> it into a string) and only afterwards send it to the browser (which is
> the preferred way to work with mod_lisp) you can at any time abort the
> page generating code and transfer control to the main loop which can
> have a handler to, say, redirect the client to an error page.

This seems to be half the issue. I'm concerned about the other half:
resuming processing after correcting the situation. This isn't so
tricky with form input that must be corrected, since the client is
doing the corrections directly. But for e.g. logging a user in, it's
not as clear how to return back to the original request that triggered
the login process.

(Of course, not in a literal sense of answering the original browser's
request, which is in the past and closed, but more of capturing enough
state about the original request to provide a positive response after
the corrective process is finished.)

> For an example see the description at
> 
>  <http://article.gmane.org/gmane.lisp.web/153>
> 
> and in particular the part about using THROW to send a response.

I hope you find the time to release this, since your other released
software has saved me a great of time and effort on my current
project. :)

Zach
From: Barry Margolin
Subject: Re: Web applications and conditions
Date: 
Message-ID: <l_v2b.401$mD.138@news.level3.com>
In article <···················@unnamed.xach.com>,
Zachary Beane  <····@xach.com> wrote:
>This seems to be half the issue. I'm concerned about the other half:
>resuming processing after correcting the situation. This isn't so
>tricky with form input that must be corrected, since the client is
>doing the corrections directly. But for e.g. logging a user in, it's
>not as clear how to return back to the original request that triggered
>the login process.

For a web application, I'm not sure that the code structure you're trying
to implement would be appropriate.

In HTTP, the way user authentication is done is that the client (e.g. web
browser) initially sends the request with no login info, and the server
sends a response back to the client that says "Sorry, you need to
authenticate."  The browser prompts the user for the username and password,
and resends the original request with this information included.

Notice that there are two independent requests involved here.  So the Lisp
notion of correcting the problem and restarting doesn't really fit in.
(Actually, you could consider there to be a Lisp-like restart, but it's
further up the stack, in the client.)

-- 
Barry Margolin, ··············@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Jock Cooper
Subject: Re: Web applications and conditions
Date: 
Message-ID: <m3llth9vrp.fsf@jcooper02.sagepub.com>
Barry Margolin <··············@level3.com> writes:

> In article <···················@unnamed.xach.com>,
> Zachary Beane  <····@xach.com> wrote:
> >This seems to be half the issue. I'm concerned about the other half:
> >resuming processing after correcting the situation. This isn't so
> >tricky with form input that must be corrected, since the client is
> >doing the corrections directly. But for e.g. logging a user in, it's
> >not as clear how to return back to the original request that triggered
> >the login process.
> 
> For a web application, I'm not sure that the code structure you're trying
> to implement would be appropriate.
> 
> In HTTP, the way user authentication is done is that the client (e.g. web
> browser) initially sends the request with no login info, and the server
> sends a response back to the client that says "Sorry, you need to
> authenticate."  The browser prompts the user for the username and password,
> and resends the original request with this information included.

Right, and this fits with how my original code snippet works.  The
browser opens the URL (with no login info), and Aserve calls the
function.  Function sees no auth info, asks for authorization and
ends.  The browser then resubmits the URL with the auth info it got
from the user.  The next time into the func the check for user
authorization will succeed, and the code continues.

You don't need to continue the original request; it fails because of
missing authorization.

Also,

> Zachary Beane  <····@xach.com> wrote:
>That is, as I understand it, part of the motivation for the condition
>system: you can write code that deals with the normal situation, and
>raise conditions matched to handlers to deal with other
>situations. You don't have to write in an excessively defensive "did
>this work? did this work? did this work?" style.

Look at the rest of the code.  This is where the no "defensive 'did
this work?  did this work?'" happens.  You are right, the condition
system allows you to avoid that.  If any error happens during the page
generation, handler-case will trap the error - you can print a stack
trace, error info, a 'continue' link, and/or whatever you want back to
the browser.  Then calling the restart lets aserve think that the page
succeeded; otherwise Aserve will try to call the function again (it
will retry a certain number of times), which is probably not what you
want if you are handling errors in your own code.

In any case Barry is right, I don't think handling the user
authorization with conditions is the best way.

Jock Cooper
--
http://www.fractal-recursions.com
From: Zachary Beane
Subject: Re: Web applications and conditions
Date: 
Message-ID: <slrnbklg38.k5m.xach@unnamed.xach.com>
In article <················@news.level3.com>, Barry Margolin wrote:
> In article <···················@unnamed.xach.com>,
> Zachary Beane  <····@xach.com> wrote:
>>This seems to be half the issue. I'm concerned about the other half:
>>resuming processing after correcting the situation. This isn't so
>>tricky with form input that must be corrected, since the client is
>>doing the corrections directly. But for e.g. logging a user in, it's
>>not as clear how to return back to the original request that triggered
>>the login process.
> 
> For a web application, I'm not sure that the code structure you're trying
> to implement would be appropriate.
> 
> In HTTP, the way user authentication is done is that the client (e.g. web
> browser) initially sends the request with no login info, and the server
> sends a response back to the client that says "Sorry, you need to
> authenticate."  The browser prompts the user for the username and password,
> and resends the original request with this information included.
> 
> Notice that there are two independent requests involved here.  So the Lisp
> notion of correcting the problem and restarting doesn't really fit in.
> (Actually, you could consider there to be a Lisp-like restart, but it's
> further up the stack, in the client.)

Actually, I was thinking of user login a la Amazon or similar. It's
not HTTP authentication exactly, but passing the right form bits to
them so they'll give you a token to pass back that identifies you as a
particular user/customer.

Zach
From: Audrey Rosée
Subject: Re: Web applications and conditions
Date: 
Message-ID: <1g0akmy.624r8snr0x70N%larve@bl0rg.net>
Hi!

Zachary Beane <····@xach.com> wrote:
> Actually, I was thinking of user login a la Amazon or similar. It's
> not HTTP authentication exactly, but passing the right form bits to
> them so they'll give you a token to pass back that identifies you as a
> particular user/customer.

I use a CLOS handler structure, with page handlers needing
authentication inheriting from auth-handler. The handlers all have a
handle-request-response method (I use araneida), which I further
decomposed into page-header and page-body. The auth-handler defines an
:around method for handle-request-method, and so checks authentication
before the children code gets to run. It can then check if the login
form was filled, if the logout form was filled or if the session cookie
is valid. In some cases it will redirect the user (when he/she has
logged out, for example) or set cookies (whe he/she has filled the login
form). Furthermore, the results of page-header are collected, so that
the auth-handler adds a "Welcome, user. Logout / Personal Info / Mails"
line to the page header.

Cheers, Audrey

-- 
From the poppy-bole
For you I stole
A little lovely dream.
From: Marc Battyani
Subject: Re: Web applications and conditions
Date: 
Message-ID: <big5ti$5no@library1.airnews.net>
"Zachary Beane" <····@xach.com> wrote

> Actually, I was thinking of user login a la Amazon or similar. It's
> not HTTP authentication exactly, but passing the right form bits to
> them so they'll give you a token to pass back that identifies you as a
> particular user/customer.

I do this with a macro (with-restricted-access or something like that IIRC).
Basically, if the autentication is not good if will reply with the HTML for
the login page otherwise it will execute the normal body to get the normal
page.

Marc
From: Ng Pheng Siong
Subject: Re: Web applications and conditions
Date: 
Message-ID: <biejt4$opp$3@mawar.singnet.com.sg>
According to Barry Margolin  <··············@level3.com>:
> In HTTP, the way user authentication is done is that the client (e.g. web
> browser) initially sends the request with no login info, and the server
> sends a response back to the client that says "Sorry, you need to
> authenticate."  The browser prompts the user for the username and password,
> and resends the original request with this information included.

This describes HTTP "Basic Authentication", which also works over HTTPS.

Another way is to use an HTML form. The server typically uses cookies to
identify sessions. In this case the server will bounce the browser to the
login form. 

Yet another way is to use SSL client certs over HTTPS. The server may do a
transparent mapping from the cert's attributes to a user name. My
ZServerSSL (for Zope) does this.

> Notice that there are two independent requests involved here.  

Yes, in the various cases the flow is the same: send request, got "bummer,
you need to authenticate", go authenticate, send request again.

It is possible for the server to record the original request as a "goto
after login" target.


-- 
Ng Pheng Siong <····@netmemetic.com> 

http://firewall.rulemaker.net  -+- Manage Your Firewall Rulebase Changes
http://www.post1.com/home/ngps -+- Open Source Python Crypto & SSL
From: Ng Pheng Siong
Subject: Re: Web applications and conditions
Date: 
Message-ID: <biejfc$opp$2@mawar.singnet.com.sg>
According to Zachary Beane  <····@xach.com>:
> In article <··············@bird.agharta.de>, Edi Weitz wrote:
> > I think this depends on how your (Lisp) web server code generally
> > handles requests. 
> 
> This seems to be half the issue. I'm concerned about the other half:
> resuming processing after correcting the situation. This isn't so
> tricky with form input that must be corrected, since the client is
> doing the corrections directly. But for e.g. logging a user in, it's
> not as clear how to return back to the original request that triggered
> the login process.

I think it still depends on your Lisp web server.

I use AllegroServe's "authorizer" interface which filters every request, so
if the authorizer says "user not logged in", it stashes the requested
target, redirects the browser to the login page, then when the user has
logged on, goes back to the page requested originally.



-- 
Ng Pheng Siong <····@netmemetic.com> 

http://firewall.rulemaker.net  -+- Manage Your Firewall Rulebase Changes
http://www.post1.com/home/ngps -+- Open Source Python Crypto & SSL
From: Edi Weitz
Subject: Re: Web applications and conditions
Date: 
Message-ID: <87oeyd8l85.fsf@bird.agharta.de>
On 25 Aug 2003 23:38:17 +0200, I wrote:

> For an example see the description at
> 
>   <http://article.gmane.org/gmane.lisp.web/153>
> 
> and in particular the part about using THROW to send a response.

I forgot to mention: One internal web site built with this framework
currently does exactly that - whenever an error occurs it generates an
HTML page with an error message and the CMUCL backtrace wrapped in a
<PRE> tag. (Now that I think of it - this reminds me of the Java
backtraces I often see on commercial websites... :)

It should be straight-forward to modify this to suit your needs if I
understand you correctly. Email me privately if you want me to send
you the code.

Edi.
From: Marc Battyani
Subject: Re: Web applications and conditions
Date: 
Message-ID: <big59u$n96@library1.airnews.net>
"Edi Weitz" <···@agharta.de> wrote
> On 25 Aug 2003 23:38:17 +0200, I wrote:
>
> > For an example see the description at
> >
> >   <http://article.gmane.org/gmane.lisp.web/153>
> >
> > and in particular the part about using THROW to send a response.
>
> I forgot to mention: One internal web site built with this framework
> currently does exactly that - whenever an error occurs it generates an
> HTML page with an error message and the CMUCL backtrace wrapped in a
> <PRE> tag. (Now that I think of it - this reminds me of the Java
> backtraces I often see on commercial websites... :)

I do that kind of stuff too, but.I send the Lisp backtrace to me by email
rather than to the user.
It's much more useful to me ;-) Generally the backtrace is enough to
understand the problem and correct it in the running Lisp image without
stopping the server.

Lisp is so cool!

Marc
From: Edi Weitz
Subject: Re: Web applications and conditions
Date: 
Message-ID: <87ptisuxzz.fsf@bird.agharta.de>
On Tue, 26 Aug 2003 19:28:38 +0200, "Marc Battyani" <·············@fractalconcept.com> wrote:

> I do that kind of stuff too, but.I send the Lisp backtrace to me by
> email rather than to the user.  It's much more useful to me ;-)
> Generally the backtrace is enough to understand the problem and
> correct it in the running Lisp image without stopping the server.

Good idea! I'll do that, too.
 
> Lisp is so cool!

Sure... :)

Take care,
Edi.
From: Matthew Danish
Subject: Re: Web applications and conditions
Date: 
Message-ID: <20030825224128.GI1454@mapcar.org>
On Mon, Aug 25, 2003 at 09:38:11AM -0500, Zachary Beane wrote:
> I'm a Lisp novice, working on one of my first web applications. I'd
> like to compose the program such that conditions indicate when there's
> an issue that needs correcting, and it's possible to do recovery
> without intermixing it with normal program flow.
> 
> For example, if I'm serving a page that requires a user to be logged
> in, I'd like to signal a condition if the page is accessed by a
> non-logged-in user, go to the login process, and return the results of
> the original request when the login process is done.
> 
> Another situation might be that a form is submitted with errors; I'd
> like to collect the errors, redisplay the form with erroroneous
> sections flagged, and continue to the next step when the errors have
> all been corrected (which may take a few iterations).

I've been using portable allegroserve to do web-applications and here's
what I do:

* Pages are denoted by name strings (such as "home")
* Pages are not directly referenced, but rather are supplied as the
  value of the continuation query parameter when a user submits an HTTP
  request to an ``action''.
* Actions, the name taken from <FORM ACTION...>, are denoted by the
  URL to which the form or link is submitted.
* Actions perform some task, and then display the given continuation
  page.
* I use the AUTHORIZER mechanism of AllegroServe for authentication,
  see the :authorizer keyword argument to PUBLISH.
* I combine all this with a template html system, running served pages
  through a pre-processor.

A small example probably will make this more lucid:

home.html:
<form action="/frob-something" method="post">
  <input type="hidden" name="continuation" value="next-page">
  <input type="text" name="something" size="5">
  <input type="submit" value="Frob">
</form>
<a href="/go?continuation=home">Reload this page</a>

actions.lisp:
(defaction frob-something ((request entity continuation) something)
  ;; do something with something ...

  ;; ... and finally
  (display-page continuation request entity))

Alternatively, you can have the macro DEFACTION supply a function to the
body which it can call, ie. DISPLAY-CONTINUATION.

(defaction go ()
  (display-continuation))

or

(defaction go (c)
  (funcall c))

The DISPLAY-PAGE function also has a parameters argument, so that you
can supply dynamic parameters to be used while pre-processing the
template.

This is a basic outline of what the DEFACTION definition looks like:

(defmacro defaction (name ((req ent cont) &rest args) &body body)
  (let ((content-type "text/html")
        (path (string-downcase (format nil "/~A" name)))
        (cgi-args (mapcar #'(lambda (a)
                              (etypecase a
                                (symbol `(,a ,(symbol-name a)))
                                (list (unless (and (symbolp (first a))
                                                   (stringp (second a)))
                                        (error "Invalid syntax: ~A" a)))))
                          args)))
    `(publish
      :path ,path
      :content-type ,content-type
      :authorizer (make-session-authorizer)
      :function
      #'(lambda (,req ,ent)
          (with-http-response (,req ,ent)
            (with-http-body (,req ,ent)
              (let ((*current-session* (get-session ,req))
                    (*current-request* ,req)
                    (,cont (get-form-value "continuation" ,req))
                    ,@(loop for (sym str) in cgi-args collect
                            `(,sym (get-form-value ,str ,req))))

                (handler-case (progn ,@body)
                  (condition (c)
                    (with-status ("~A" c)
                      (display-page ,cont ,req ,ent)))))))))))

MAKE-SESSION-AUTHORIZER creates a session-authorizer object as described
in the AllegroServe reference.  GET-SESSION is a related function.

GET-FORM-VALUE is a simple wrapper around AllegroServe's functions to do
this.

WITH-STATUS is a macro which sets a particular parameter in this system,
a status bar.  You don't have to do this, but the default behavior here
is to aesthetically print the condition to the status bar.

Obviously, each action could have it's own HANDLER-CASE if it wanted to
do something other than the default.  But here, I've already got a
system which can report human-understandable errors to the user, without
very much effort at all.

-- 
; Matthew Danish <·······@andrew.cmu.edu>
; OpenPGP public key: C24B6010 on keyring.debian.org
; Signed or encrypted mail welcome.
; "There is no dark side of the moon really; matter of fact, it's all dark."