The Factor web framework was recently redeveloped and my previous post on the topic is out of date. I'm re-learning how to write Factor web applications as I work through a simple app I'm building.
At first I was overwhelmed by the large number of features in the new framework, but once I got a bit of an understanding I was pleasantly surprised how easy it was to put an application together.
Starting the web server hasn't changed much. Start a web server running on port 8888 in a background thread:
USING: http.server threads ;
[ 8888 httpd ] in-thread
A web application is written as a bunch of responders. Each responder handles a request from the client browser. An easy way of creating a responder is to create an 'action' object. An 'action' object has a number of slots, one of which is called 'display'. Setting 'display' to a quotation will cause that quotation to be called when the client browser makes a request. The quotation should return a 'response' object. The 'response' object defines what gets sent back to the client browser. The 'body' slot of the 'response' object can be a string which is returned to the browser as the contents of the request. So a basic responder is:
<action> [
<response>
200 >>code
"text/plain" >>content-type
"Hello World" >>body
] >>display
The web browser uses a global variable called 'main-responder' as the first responder to try when a request is made. If we set 'main-responder' to the value of our responder then it immediately becomes available to a web request. So our entire simple web application is:
USING: http http.server threads furnace.actions ;
[ 8888 httpd ] in-thread
<action> [
<response>
200 >>code
"text/plain" >>content-type
"Hello World" >>body
] >>display main-responder set-global
Visiting http://localhost:8888/ should display a page with 'Hello World' as the text.
A responder that works just from the root of the URL is not very practical. A dispatcher is an object that can acts as a responder that calls other responders based on part of the URL. Here's a dispatcher that calls one responder when /hello is requested, and another when /goodbye is requested.
USING:
furnace.actions
http
http.server
http.server.dispatchers
threads
;
[ 8888 httpd ] in-thread
TUPLE: my-dispatcher < dispatcher ;
my-dispatcher new-dispatcher
<action> [
<response>
200 >>code
"text/plain" >>content-type
"Hello World" >>body
] >>display "hello" add-responder
<action> [
<response>
200 >>code
"text/plain" >>content-type
"Goodbye World" >>body
] >>display "goodbye" add-responder
main-responder set-global
First I create a tuple called 'my-dispatcher' that inherits from the base dispatcher class. The I create an instance of this using 'new-dispatcher', create my actions and add them under the paths 'hello' and 'goodbye'.
Writing response objects gets a bit tedious. Factor provides two template systems that can be used to make writing web applications easier. One is 'fhtml' which existed in the previous web framework incarnation. The other, chloe, is an XML based templating language, and is covered quite well in the Factor documentation.
To create an action that uses a Chloe template, create a 'page-action' instead of an 'action'. Set the 'template' slot of the page-action to be a two element array containing the dispatcher class and the template filename (without the .xml extension):
USING:
furnace.actions
html.templates.chloe
http
http.server
http.server.dispatchers
threads
;
[ 8888 httpd ] in-thread
TUPLE: my-dispatcher < dispatcher ;
my-dispatcher new-dispatcher
<page-action>
{ my-dispatcher "main" } >>template
"foo" add-responder
main-responder set-global
When this '/foo' is accessed on the webserver, this action will run. It will look for main.xml which needs to be a chloe template file. Here's a simple example template:
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<html>
<body>
<p>Hello!</p>
</body>
</html>
</t:chloe>
The chloe help goes into a lot of detail about how to use the template to substitute variable values, etc. I'll cover that in a later post.
The web framework has a lot of other features. Sessions, Authentication, Database, Validation, etc. Hopefully this will help you get started. All the example above can be entered and run (barring typo's and unescaped html entities) in a Factor listener. Download it and try it!
Update: There is a wiki page on the concatenative wiki with more information about the web framework.