WebApp
Overview
WARNING: the webapp framework is still an early prototype, so will be going through many changes during development
The webapp pod defines a framework for building web applications by assembling prebuilt WebSteps
:
- FindResourceStep: maps the request URI to a Fan obj which represents a resource in the VM's local UriSpace
- FindViewStep: find a suitable Weblet which can service the request and provide a representation of the resource
- FindChromeStep: lets you plug in a common look and feel across all your views
- ServiceViewStep: does it
- LogStep: logs using W3C extended log format
FindResourceStep
FindResourceStep
is responsible for mapping the web request URI to a Fan object which represents the the resource to service. This object can be anything, but is typically a File or an object in the domain model (such as a row in the database). In most cases, the web request URI is mapped directly to the local UriSpace. But FindResourceStep
also provides the following features:
- Defines the home page resource
- Searches for file extensions
- Searches for the index file to use for a directory
- Redirects to use trailing slash on directories
- Returns 404 if resource isn't found
Let's take an example:
// pipeline step config FindResourceStep { extSearch = ["fan", "html", "txt"] dirIndex = ["index.fan", "index.html"] } // setup UriSpace.root.create(`/homePage`, scriptDir + `home.html`) UriSpace.mount(`/dir`, UriSpace.makeDir(scriptDir + `dir/`)) // application directory boot.fan home.html dir/ index.fan subdir/ index.html script.fan test.txt
Since we didn't configure the homePage
field, it defaults to "/homePage", which is the resource object we'll use for servicing "/". In our setup we map "/homePage" to the "home.html" file in the same directory as our boot script.
Since we configured extSearch
, we can use the following URIs with or without extensions:
/dir/index => /dir/index.fan /dir/index.fan => /dir/index.fan /dir/subdir/script => /dir/subdir/script.fan /dir/subdir/script.fan => /dir/subdir/script.fan /dir/subdir/test => /dir/subdir/test.txt /dir/subdir/test.txt => /dir/subdir/test.txt
When processing directories we use the dirIndex
field to search the directory for its index file. We also check if the web request is accessing a directory URI without a trailing slash, in which case we do a redirect to keep relative hrefs working:
/dir => redirects to /dir/ /dir/ => /dir/index.fan /dir/subdir => redirects to /dir/subdir/ /dir/subdir/ => /subdir/index.html
If you want your own resource objects to take advantage of the trailing slash redirect, you need only declare a isDir()
method which return true.
FindViewStep
FindViewStep
is responsible for finding a Weblet
to service the request by returning an appropriate representation of the resource found by FindResourceStep
.
If the resource is itself a Weblet
, then the view is the resource itself. Otherwise the type database is queried for a type which declares itself a "webView" on the resource type. For example:
@webView=Invoice# class InvoiceView : Weblet {}
File resources are automatically handled by the web::FileWeblet
which handles all the dirty details for cache control, modification time, ETags, etc.
If there are multiple web views registered on the resource, then we look for the "webViewPriority" facet which is an Int to find the one with the highest priority. If a view doesn't have a "webViewPriority" facet, then it is assumed to have a priority of zero. For example given two views registered on Invoice, the default view would be CustomInvoiceView:
@webView=Invoice# class DefaultInvoiceView : Weblet {} @webView=Invoice# @webViewPriority=10 class CustomInvoiceView : Weblet {}
You can specify an explicit view using the "view" query parameter where the value is the view type qname:
/invoices/invoice1234?view=acme::DefaultInvoiceView
FindChromeStep
FindChromeStep
is used to create a pluggable look and feel or theme for a web application. The chrome is just a normal webapp::Widget
which wraps the view Widget
. If the view isn't a Widget
, then the chrome has no effect.
ServiceViewStep
ServiceViewStep
class is a simple class which calls Weblet.service
on the view weblet.
LogStep
LogStep
class is used to generate a server log file for all HTTP requests in the W3C Extended Log File Format. The file
Uri must be configured - records are always appended to this file. Logging is done on the onAfterService
callback.
The fields
property configures the format of the log records. It is a string of field names separated by a space. The following field names are supported:
- date: UTC date as DD-MM-YYYY
- time: UTC time as hh:mm:ss
- c-ip: the numeric IP address of the remote client socket
- c-port: the IP port of the remote client socket
- cs-method: the request method such as GET
- cs-uri: the encoded request uri (path and query)
- cs-uri-stem: the encoded path of the request uri
- cs-uri-query: the encoded query of the request uri
- sc-status: the return status code
- time-taken: the time taken to process request in milliseconds
- cs(HeaderName): request header value such
If any unknown fields are specified or not available then "-" is logged. The default format is:
date time c-ip cs-method cs-uri-stem cs-uri-query sc-status time-taken cs(User-Agent) cs(Referer)
Example log record with this format:
18-04-2008 01:42:54 127.0.0.1 GET / - 304 1 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13" "http://localhost:8080/dir/index.html"