8.16.0.4
Peony Web Framework🔗ℹ
Peony is a small frontend on Web Applications in Racket
which makes web development fit closer to the idiom familiar to people who’ve mainly
used php or similar in the past. With Peony, each webpage lives at a fixed path and
corresponds to a chunk of code that’s executed to produce html when the page is accessed.
As long as that’s all that’s needed, the details of dispatching and such can be avoided.
Peony is under active development and the api should not be considered stable.
See also DB: Database Connectivity.
As an example of use, say we want to create a webapp to demonstrate the use of GET and POST parameters.
First, we can set up a main page like so:
(define index (page index.html |
'(html (body (h1 "demo webapp") |
(a [[href "demos/get"]] "test get") |
(br) |
(a [[href "demos/post"]] "test post"))))) |
Which defines
index as a
webpage with the path
index.html and a static body. Since we’ll be using it as
the
index of our webapp it will be accessible at www.address.com/. we call it
index.html
just for the sake of tradition, since it has to have a non-empty name as well.
The GET page can be implemented using the GET binding like so:
(define getpage (page demos/get |
`(html (body (h1 "GET demo") |
(form [[method "get"]] |
(p "submit a GET request with paramenters:") |
(p "p1: " (input [[type "text"][name "p1"]])) |
(p "p2: " (input [[type "text"][name "p2"]])) |
(br) |
(button "submit")) |
(table |
(tr (th "Name") (th "Value")) |
,@(hash-map GET (λ (key val) `(tr (td ,key) (td ,val))))) |
(a [[href "index.html"]] "back"))))) |
(where ",@" is
unquote-splicing). The resulting page is generated with new GET values each time it’s loaded.
The POST page can be identical except for replacing each instance of ’get’ with ’post’ (noting that the one referencing the
actual binding must be all-caps POST):
(define postpage (page demos/post |
`(html (body (h1 "POST demo") |
(form [[method "post"]] |
(p "submit a POST request with paramenters:") |
(p "p1: " (input [[type "text"][name "p1"]])) |
(p "p2: " (input [[type "text"][name "p2"]])) |
(br) |
(button "submit")) |
(table |
(tr (th "Name") (th "Value")) |
,@(hash-map POST (λ (key val) `(tr (td ,key) (td ,val))))) |
(a [[href "index.html"]] "back"))))) |
Then we can put it all together into a
webapp and serve it:
serve/servlet with
#:servlet-regexp #rx"" and
#:servlet-path ""
is equivalent to
serve/dispatch, but doing it this way allows us to add other
serve/servlet-specific parameters later (such as
#:start-web-browser?
and
#:port).
Returns a servlet that can be passed to
Web Applications in Racket’s
serve/dispatch (or similar, but the
servlet-regexp
must be
#rx"" in any case). The servlet serves each page at
the url matching its
path, and a fixed 404 page at all
other paths. The first page, the
index, is also served at the empty path.
A structure for webpages, consisting of a path the page can be found at and a function
mapping an http
request to a
response. The forms below generate slightly
limited
webpages automatically.
2.1 Defining Pages🔗ℹ
Returns a
webpage whose path is
name and whose body is
a
Web Applications in Racket servlet - a
function taking an http
request and returning a
response.
The response is a standard 200 response whose body consists of the html
corresponding to the
xexpr? provided by
contents. The
contents expression
has access to four values:
GET,
POST,
COOKIE and
REQ,
containing respectively a hash mapping GET arguments to their values, a hash mapping
POST arguments to their values, a hash mapping cookies to their values, and the literal
request in full.
In GET, POST and COOKIE, if there are multiple parameters with the same name then the earlier ones are
shadowed by the later ones, the full query can be extracted by manually processing
REQ if this is a problem.
(page name contents) is a shorthand for (page/proto name (hash "Content-Type" "text/html; charset=utf-8") (list (string->bytes/utf-8 (xexpr->string contents)))).
Returns a
webpage whose path is
name and whose body is the string provided in the
contents expression.
As with
page, the expression for
contents has access to
GET,
POST,
COOKIE and
REQ.
(textpage name contents) is a shorthand for a page/proto expression.
Returns a
webpage whose path is
name and whose body is the bytestring data provided by the
contents expression, served with the
specified
mime-type. Again, based on page/proto.
Works like
page, but with an additional
headers expression. The
headers expression has access to GET, POST, COOKIE and REQ like
contents does, and it should return a hash of strings to strings describing any HTTP headers that should be set in the response. These headers
override any implicit headers (ie. the default Content-Type of ’text/html; charset=utf-8’ and those mentioned in the documentation for
response).
To
datapage as
page/headers is to
page. Note that any Content-Type specified in the headers will override the
mime-type argument.
This is a generic form to construct pages. It works like
page/headers and
textpage/headers, but without making any assumptions about the nature
and purpose of the contents or what Content-Type is appropriate (a list of bytestrings is the form that web-server servlets expect page contents to be in).
Again, the
headers hash should take strings to strings.