Peony Web Framework
1 Full Example
2 Reference
page
textpage
page-proto
response/  data
webapp
webpage
7.7

Peony Web Framework

 (require peony) package: peony

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.

See also DB: Database Connectivity.

1 Full Example

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:
(define app (webapp index getpage postpage))
(serve/servlet app #:servlet-regexp #rx"" #:servlet-path "")
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).

2 Reference

syntax

(page name contents)

 
  name : symbol?
  contents : xexpr?
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 contents response/xexpr).

syntax

(textpage name contents)

 
  name : symbol?
  contents : string?
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 (proto-page name (string->bytes/utf-8 contents) (curry response/data #"text/plain; encoding=utf-8")).

syntax

(page-proto name contents response-operation)

 
  name : symbol?
  contents : string?
  response-operation : (-> any? response?)
This is a generic form to construct pages. It works like page and textpage, but it takes an additional argument response-operation that transforms the result of the contents expression into a response to pass back to the client. For instance, if you wanted a way to specify the return code of your pages, you could write something like:
(define-syntax (page/return-code stx)
  (define dtm (syntax->datum stx))
  (datum->syntax stx
                 (page-proto ,(second dtm)
                             ,(fourth dtm)
                             (curry response/xexpr #:code ,(third dtm)))))
And invoke it:
(page/return-code secret.html
                  (if authorised? 200 403)
                  (if authorised? page-contents '(html (body (h1 "Access Denied")))))
Note that it must be a syntax form and not a function, because page-proto must receive its arguments unevaluated.

procedure

(response/data mime-type bs)  response

  mime-type : bytes?
  bs : bytes?
Returns an http 200 response containing the given bytes bs labelled with the given mime-type. Intended for use with page-proto to construct page types that serve data of kinds not yet supported.

procedure

(webapp index pages ...)  (-> request? response?)

  index : webpage?
  pages : webpage?
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.

struct

(struct webpage (path body)
    #:extra-constructor-name make-webpage)
  path : symbol?
  body : (-> request? response?)
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 page form generates slightly limited webpages automatically.