8.18
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.