I've got a question about the *dispatch-table* and the associated actions that are returned from the dispatch functions. Based on the code in modlisp.lisp:
(loop for dispatcher in *dispatch-table* for action = (funcall dispatcher *request*) when action return (funcall action) finally (setf (return-code *reply*) +http-not-found+))))
Might it be desirable to check the return of (funcall action), and if nil, continue processing with the next dispatcher rather than returning and sending output at this point? If one truly wanted to return an empty body, one could still throw to the tbnl-handler-done tag which would bypass the rest of the dispatch table.
I'm writing a template dispatcher which will process a request using HTML-TEMPLATE. Of course, this is only possible if I can map a request to a template (I may store the templates on the filesystem or in a db, not sure yet). If a template cannot be found for the request, then I'd like to fall back to processing the request with the rest of the dispatch table (which may just serve a static file or may be another template dispatcher thats resolves templates differently).
In order to accomplish the above in the today's TBNL, I need to make the template resolution occur in the dispatch function (instead of the action function that the dispatcher returns) because I can return nil from the dispatcher if resolution fails and thus instruct TBNL to move on to the next dispatcher. In addition, I'd also like to make my dispatch decision based on a prefix. So I'd like to use create-prefix-dispatcher to limit the template processing to only part of the URI namespace. Thus, I need two 'tests' to occur in my dispatch function: 1) is there a prefix match (replicating the create-prefix-dispatcher functionality), and 2) does the request map to a template. With that said, I have created:
(defun create-template-dispatcher (prefix resolver-function) #'(lambda (request) (when (prefix-match-p prefix (script-name request)) (let ((template (funcall resolver-function request))) (when template #'(lambda () (funcall *default-template-handler* template)))))))
prefix-match-p is refactored code from create-prefix-dispatcher, but is essentially the same, does the request start with the given prefix? If it does not, I return nil, and processing continues with the rest of the dispatch table. Assuming the prefix does match, I then go on to call the template resolution function (resolver-function) which returns a template as a string or nil if a template could not be found. Template resolution might query a db or just read a template from the filesystem. Finally, if a template if found, I process the template with the default template handler which points to:
(defun template-handler (template) (let ((context (assemble-context))) (with-output-to-string (*default-template-output*) (fill-and-print-template template context))))
Here is a sample template resolver function that maps a request to a template on the filesystem (only works on systems with '/' as a separator, my lisp path manipulations skills are absent at the moment):
(defun filesystem-resolver (base &optional (request *request*)) (contents-of-file (parse-namestring (concatenate 'string base (script-name request)))))
And here is my sample dispatch table that pulls templates from multiple locations on the filesystem depending on the request URI:
(setf *dispatch-table* (list (create-template-dispatcher "/tbnl/space1" #'(lambda (request) (filesystem-resolver "/tmp" request))) (create-template-dispatcher "/tbnl/space2" #'(lambda (request) (filesystem-resolver "/var/www/html" request))) #'default-dispatcher))
Back to the original question and my rationale for asking it, I think I'd prefer if I didn't have to put all of my predicates for testing whether or not the template handler should be run within the dispatch function as it just doesn't seem to be the right spot for this logic. However, I definitely want the template handler executed based on the prefix. If TBNL could continue processing the dispatch table upon the return of nil from the handler (action), my dispatch table would look like:
(setq *dispatch-table* (nconc (mapcar (lambda (args) (apply #'create-prefix-dispatcher args)) '(("/tbnl/space1" template-dispatcher) ("/tbnl/space2" template-dispatcher))) (list #'default-dispatcher)))
And the template-dispatcher would consult a *resolver-table* and invoke the appropriate resolution function for the request. The nice thing about this approach is that my *dispatch-table* is not cluttered with non-dispatch related stuff such as template resolution. Of course that all requires the ability for an action (the template-dispatcher above) to signify that it cannot handle the request by returning a nil, which could then indicate to TBNL that the next entry in the *dispatch-table* should be tried. Would this functionality appeal to anyone else?
Sorry about the length of email, but I thought it would be useful to share what I'm doing with TBNL. This is all just for fun and non-profit and my pursuit of learning lisp so if I've said something stupid or more specifically am not doing things in the traditional "lisp way", please let me know.
Thanks, Pete
Hi!
I don't know. A dispatcher is a dispatcher. Its task is to find the right handler for the request.
And a handler returning NIL indicates an error. TBNL should emit an internal server error 500.
On the other hand: Internal server errors are a bad thing. Better produce a 404 instead of an 500. The handler can't handle the request? Let the handler report this if it's no real problem/error. If no dispatcher is found and no handler "feels fit" to handle the request, then a 404 is the right answer.
But I think it should be an exception to the rule. Let the handler return _two_ values if he is sure that the reason for not processing the request is no fatal error and dispatching should continue.
Normal handlers just return a string or nil if they have a problem. Handlers which are aware of the possibility that other dispatchers could catch the request when they fail return (values nil t)
Regards, Stefan
On Fri, 2004-07-23 at 09:10, Stefan Scholl wrote:
But I think it should be an exception to the rule. Let the handler return _two_ values if he is sure that the reason for not processing the request is no fatal error and dispatching should continue. Normal handlers just return a string or nil if they have a problem. Handlers which are aware of the possibility that other dispatchers could catch the request when they fail return (values nil t)
Thats a good idea. I keep forgetting about the usefulness of the ability to return multiple values. With this ability, I would be able to minimize the complexity of my dispatch functions and move some of that logic into the handler itself which could then have the option of opting to not process the request (defer to the next dispatcher).
Out of curiousity, what do you guys like to use for generating your HTML? Are you firm believers in the whole "separation of logic and presentation" and prefer the use of template engines? Or do you prefer to output HTML from your TBNL apps and then let folks use CSS to adjust the presentation as they see fit? Or do you use a combination of both?
Thanks, Pete
On Fri, 23 Jul 2004 09:36:57 -0400, Pete Kazmier pete-tbnl-dev@kazmier.com wrote:
On Fri, 2004-07-23 at 09:10, Stefan Scholl wrote:
But I think it should be an exception to the rule. Let the handler return _two_ values if he is sure that the reason for not processing the request is no fatal error and dispatching should continue. Normal handlers just return a string or nil if they have a problem. Handlers which are aware of the possibility that other dispatchers could catch the request when they fail return (values nil t)
Thats a good idea. I keep forgetting about the usefulness of the ability to return multiple values. With this ability, I would be able to minimize the complexity of my dispatch functions and move some of that logic into the handler itself which could then have the option of opting to not process the request (defer to the next dispatcher).
I'm not sure what would be the right way. At the moment it is not specified what it means if a handler returns NIL so we could as well implement Pete's initial idea to let the next dispatcher take over in this case. Using a second return value would also be a possibility but it would require more work in modlisp.lisp because the HANDLER-BIND code already generates a second return value to denote errors.
Out of curiousity, what do you guys like to use for generating your HTML? Are you firm believers in the whole "separation of logic and presentation" and prefer the use of template engines? Or do you prefer to output HTML from your TBNL apps and then let folks use CSS to adjust the presentation as they see fit? Or do you use a combination of both?
I've written HTML-TEMPLATE for separation of logic and presentation. Not because I think it's "the way to go" but mostly because I sometimes have to work with web designers (graphical artists) who I can't bother to modify my Lisp code directly. It turned out that I use it very rarely - I think it's clumsy. I usually use CL-WHO.
I personally think that "separation of logic and presentation" is a good idea in theory but it rarely works in practice, unfortunately.
Cheers, Edi.