Updating docs

This commit is contained in:
Jay McCarthy 2010-11-27 21:37:05 -05:00
parent 9b066d0f04
commit a28cf7df10
23 changed files with 266 additions and 268 deletions

View File

@ -2,6 +2,7 @@
(require racket/runtime-path
net/url
web-server/private/xexpr
web-server/http/xexpr
web-server/http/response-structs
web-server/http/request-structs)
@ -24,22 +25,23 @@
"web-server/default-web-root/htdocs/error.css"))
(define (pretty-exception-response url exn)
`(html
(head
(title "Servlet Error")
(link ([rel "stylesheet"] [href "/error.css"])))
(body
(div ([class "section"])
(div ([class "title"]) "Exception")
(p
"The application raised an exception with the message:"
(pre ,(if (exn:pretty? exn)
(exn:pretty-xexpr exn)
(exn-message exn))))
(p
"Stack trace:"
,(format-stack-trace
(continuation-mark-set->context (exn-continuation-marks exn))))))))
(response/xexpr
`(html
(head
(title "Servlet Error")
(link ([rel "stylesheet"] [href "/error.css"])))
(body
(div ([class "section"])
(div ([class "title"]) "Exception")
(p
"The application raised an exception with the message:"
(pre ,(if (exn:pretty? exn)
(exn:pretty-xexpr exn)
(exn-message exn))))
(p
"Stack trace:"
,(format-stack-trace
(continuation-mark-set->context (exn-continuation-marks exn)))))))))
; file-response : nat str str [(cons sym str) ...] -> response

View File

@ -10,4 +10,4 @@
200 #"Okay"
(current-seconds) TEXT/HTML-MIME-TYPE
empty
(list (include-template "static.html"))))
(list (string->bytes/utf-8 (include-template "static.html")))))

View File

@ -1,8 +1,13 @@
#lang racket
(require web-server/templates)
(require web-server/templates
web-server/http)
(provide (all-defined-out))
(define interface-version 'v1)
(define timeout +inf.0)
(define (start initial-request)
(response/template (include-template "static.html")))
(response/full
200 #"Okay"
(current-seconds) TEXT/HTML-MIME-TYPE
empty
(list (string->bytes/utf-8 (include-template "static.html")))))

View File

@ -1,5 +1,6 @@
#lang racket
(require web-server/templates
web-server/http
xml)
(provide (all-defined-out))
(define interface-version 'v1)

View File

@ -5,7 +5,7 @@
(require "util.rkt"
web-server/http)
(provide/contract
[read-mime-types (path-string? . -> . hash?)]
[read-mime-types (path-string? . -> . (hash/c symbol? bytes?))]
[make-path->mime-type (path-string? . -> . (path? . -> . bytes?))])
; read-mime-types : path? -> hash-table?

View File

@ -3,6 +3,7 @@
@title[#:tag "servlet-structs"]{Common Contracts}
@(require (for-label web-server/servlet/servlet-structs
web-server/http
web-server/servlet))
@defmodule[web-server/servlet/servlet-structs]{
@ -19,10 +20,11 @@ Example: @racket["http://localhost:8080/servlets;1*1*20131636/examples/add.rkt"]
Equivalent to @racket[(k-url? . -> . response?)].
Example: @racketblock[(lambda (k-url)
`(html
(body
(a ([href ,k-url])
"Click Me to Invoke the Continuation!"))))]
(response/xexpr
`(html
(body
(a ([href ,k-url])
"Click Me to Invoke the Continuation!")))))]
}
@defthing[expiration-handler/c contract?]{
@ -31,10 +33,11 @@ Equivalent to @racket[(or/c false/c (request? . -> . response?))].
Typically @racket[#f] uses the default expiration handler, which displays an error message.
Example: @racketblock[(lambda (req)
`(html (head (title "Expired"))
(body (h1 "Expired")
(p "This URL has expired. "
"Please return to the home page."))))]
(response/xexpr
`(html (head (title "Expired"))
(body (h1 "Expired")
(p "This URL has expired. "
"Please return to the home page.")))))]
}
@defthing[embed/url/c contract?]{

View File

@ -1,5 +1,12 @@
#lang scribble/doc
@(require "web-server.rkt")
@(require "web-server.rkt"
(for-label web-server/http
web-server/private/servlet
web-server/configuration/responders
web-server/dispatchers/filesystem-map
web-server/servlet/setup
net/url
web-server/managers/manager))
@title[#:tag "dispatch-servlets"]{Serving Servlets}
@a-dispatcher[web-server/dispatchers/dispatch-servlets

View File

@ -431,19 +431,20 @@ These @tech{formlet}s are the main combinators for form input.
@section{Utilities}
@(require (for-label web-server/formlets/servlet))
@(require (for-label web-server/formlets/servlet
web-server/http))
@defmodule[web-server/formlets/servlet]{
A few utilities are provided for using @tech{formlet}s in Web applications.
@defproc[(send/formlet [f (formlet/c any/c ...)]
[#:wrap wrapper
(xexpr/c . -> . response?)
(xexpr/c . -> . xexpr/c)
(lambda (form-xexpr)
`(html (head (title "Form Entry"))
(body ,form-xexpr)))])
(values any/c ...)]{
Uses @racket[send/suspend] to send @racket[f]'s rendering (wrapped in a FORM tag whose action is
Uses @racket[send/suspend] and @racket[response/xexpr] to send @racket[f]'s rendering (wrapped in a FORM tag whose action is
the continuation URL (wrapped again by @racket[wrapper])) to the client.
When the form is submitted, the request is passed to the
processing stage of @racket[f].

View File

@ -5,7 +5,7 @@
@defmodule[web-server/http]
The @web-server implements many HTTP RFCs that are provided by this module.
The @web-server implements many HTTP libraries that are provided by this module.
@; ------------------------------------------------------------
@section[#:tag "request-structs"]{Requests}
@ -159,35 +159,37 @@ Here is an example typical of what you will find in many applications:
@defmodule[web-server/http/response-structs]{
@defstruct[response/basic
@defstruct*[response
([code number?]
[message bytes?]
[seconds number?]
[mime bytes?]
[headers (listof header?)])]{
A basic HTTP response containing no body. @racket[code] is the response code,
[headers (listof header?)]
[output (output-port? . -> . void)])]{
An HTTP response where @racket[output] produces the body. @racket[code] is the response code,
@racket[message] the message, @racket[seconds] the generation time, @racket[mime]
the MIME type of the file, and @racket[extras] are the extra headers, in addition
to those produced by the server.
Example:
@racketblock[
(make-response/basic
(response
301 #"Moved Permanently"
(current-seconds) TEXT/HTML-MIME-TYPE
(list (make-header #"Location"
#"http://racket-lang.org/downloads")))
#"http://racket-lang.org/downloads"))
(λ (op) (write-bytes #"Moved" op)))
]
}
@defstruct[(response/full response/basic)
([body (listof bytes?)])]{
As with @racket[response/basic], except with @racket[body] as the response
body.
@defproc[(response/full [code number?] [message bytes?] [seconds number?] [mime bytes?]
[headers (listof header?)] [body (listof bytes?)])
response?]{
A constructor for responses where @racket[body] is the response body.
Example:
@racketblock[
(make-response/full
(response/full
301 #"Moved Permanently"
(current-seconds) TEXT/HTML-MIME-TYPE
(list (make-header #"Location"
@ -199,83 +201,6 @@ Here is an example typical of what you will find in many applications:
#"</p></body></html>"))
]
}
@defstruct[(response/port response/basic)
([output (output-port? . -> . void)])]{
As with @racket[response/basic], except where @racket[output] generates the response
body. This response type is not as safe and efficient for clients as @racket[response/incremental],
but can be convenient on the server side.
Example:
@racketblock[
(make-response/full
301 #"Moved Permanently"
(current-seconds) TEXT/HTML-MIME-TYPE
(list (make-header #"Location"
#"http://racket-lang.org/downloads"))
(λ (op)
(write-bytes #"<html><body><p>" op)
(write-bytes #"Please go to <a href=\"" op)
(write-bytes #"http://racket-lang.org/downloads" op)
(write-bytes #"\">here</a> instead." op)
(write-bytes #"</p></body></html>" op)))
]
}
@defstruct[(response/incremental response/basic)
([generator ((() () #:rest (listof bytes?) . ->* . any) . -> . any)])]{
As with @racket[response/basic], except with @racket[generator] as a function that is
called to generate the response body, by being given an @racket[output-response] function
that outputs the content it is called with. If the @racket[output-response] function is called
with arguments of zero length (when concatenated), then the output port is flushed with
@racket[flush-output].
Here is a short example:
@racketblock[
(make-response/incremental
200 #"OK" (current-seconds)
#"application/octet-stream"
(list (make-header #"Content-Disposition"
#"attachment; filename=\"file\""))
(lambda (output-response)
(output-response #"Some content")
(output-response)
(output-response #"Even" #"more" #"content!")
(output-response #"Now we're done")))
]
}
@defthing[response/c contract?]{
Equivalent to
@racketblock[
(or/c response/basic?
(cons/c bytes? (listof (or/c string? bytes?)))
xexpr/c)
]
}
@defproc[(make-xexpr-response [xexpr xexpr/c]
[#:code code number? 200]
[#:message message bytes? #"Okay"]
[#:seconds seconds number? (current-seconds)]
[#:mime-type mime-type bytes? TEXT/HTML-MIME-TYPE]
[#:headers headers (listof header?) empty]
[#:preamble preamble bytes? #""])
response/full?]{
Equivalent to
@racketblock[
(make-response/full
code message seconds mime-type headers
(list preamble (string->bytes/utf-8 (xexpr->string xexpr))))
]}
@defproc[(normalize-response [response response/c] [close? boolean? #f])
(or/c response/full? response/incremental? response/port?)]{
Coerces @racket[response] into a full response, filling in additional details where appropriate.
@racket[close?] represents whether the connection will be closed after the response is sent (i.e. if HTTP 1.0 is being used.) The accuracy of this only matters if
@racket[response] is a @racket[response/incremental?].
}
@defthing[TEXT/HTML-MIME-TYPE bytes?]{Equivalent to @racket[#"text/html; charset=utf-8"].}
@ -289,6 +214,7 @@ transmission that the server @bold{will not catch}.}
@(require (for-label net/cookie
web-server/servlet
web-server/http/xexpr
web-server/http/redirect
web-server/http/request-structs
web-server/http/response-structs
@ -311,12 +237,6 @@ transmission that the server @bold{will not catch}.}
Constructs a header that sets the cookie.
}
@defproc[(xexpr-response/cookies [cookies (listof cookie?)]
[xexpr xexpr/c])
response/full?]{
Constructs a response using @racket[xexpr] that sets all the cookies in @racket[cookies].
}
Examples:
@racketblock[
(define time-cookie
@ -332,11 +252,11 @@ transmission that the server @bold{will not catch}.}
(list time-cookie id-cookie)))
(send/suspend
(lambda (k-url)
(xexpr-response/cookies
(list time-cookie id-cookie)
`(html (head (title "Cookie Example"))
(body (h1 "You're cookie'd!"))))))
(lambda (k-url)
(response/xexpr
#:cookies (list time-cookie id-cookie)
`(html (head (title "Cookie Example"))
(body (h1 "You're cookie'd!"))))))
]
@warning{When using cookies, make sure you follow the advice of the @link["http://cookies.lcs.mit.edu/"]{MIT Cookie Eaters},
@ -347,6 +267,7 @@ transmission that the server @bold{will not catch}.}
@section[#:tag "cookie-parse"]{Extracting Cookies}
@(require (for-label web-server/http/cookie-parse
web-server/http/xexpr
net/cookie
net/url
racket/list))
@ -384,10 +305,11 @@ transmission that the server @bold{will not catch}.}
(cookie->header (make-cookie "id" "joseph"))))))
(define (hello who)
`(html (head (title "Hello!"))
(body
(h1 "Hello "
,who))))
(response/xexpr
`(html (head (title "Hello!"))
(body
(h1 "Hello "
,who)))))
]
}
@ -424,7 +346,8 @@ transmission that the server @bold{will not catch}.}
@; ------------------------------------------------------------
@section[#:tag "basic-auth"]{Basic Authentication}
@(require (for-label web-server/http/basic-auth))
@(require (for-label web-server/http/response-structs
web-server/http/basic-auth))
@defmodule[web-server/http/basic-auth]{
@ -449,21 +372,25 @@ web-server/insta
(define (start req)
(match (request->basic-credentials req)
[(cons user pass)
`(html (head (title "Basic Auth Test"))
(body (h1 "User: " ,(bytes->string/utf-8 user))
(h1 "Pass: " ,(bytes->string/utf-8 pass))))]
(response/xexpr
`(html (head (title "Basic Auth Test"))
(body (h1 "User: " ,(bytes->string/utf-8 user))
(h1 "Pass: " ,(bytes->string/utf-8 pass)))))]
[else
(make-response/basic
(response
401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
(list
(make-basic-auth-header
(format "Basic Auth Test: ~a" (gensym)))))]))
(format "Basic Auth Test: ~a" (gensym))))
void)]))
]
}
@; ------------------------------------------------------------
@section[#:tag "digest-auth"]{Digest Authentication}
@(require (for-label web-server/http/digest-auth
web-server/http/xexpr
web-server/http/response-structs
racket/pretty))
@defmodule[web-server/http/digest-auth]{
@ -518,20 +445,48 @@ web-server/insta
(define (start req)
(match (request->digest-credentials req)
[#f
(make-response/basic
(response
401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
(list (make-digest-auth-header
(format "Digest Auth Test: ~a" (gensym))
private-key opaque)))]
private-key opaque))
void)]
[alist
(define check
(make-check-digest-credentials
(password->digest-HA1 (lambda (username realm) "pass"))))
(define pass?
(check "GET" alist))
`(html (head (title "Digest Auth Test"))
(body
(h1 ,(if pass? "Pass!" "No Pass!"))
(pre ,(pretty-format alist))))]))
(response/xexpr
`(html (head (title "Digest Auth Test"))
(body
(h1 ,(if pass? "Pass!" "No Pass!"))
(pre ,(pretty-format alist)))))]))
]
}
@; ------------------------------------------------------------
@section[#:tag "xexpr"]{X-expression Support}
@(require (for-label web-server/http/xexpr
xml))
@defmodule[web-server/http/xexpr]{
@defproc[(response/xexpr [xexpr xexpr/c]
[#:code code number? 200]
[#:message message bytes? #"Okay"]
[#:seconds seconds number? (current-seconds)]
[#:mime-type mime-type bytes? TEXT/HTML-MIME-TYPE]
[#:headers headers (listof header?) empty]
[#:cookies cookies (listof cookie?) empty]
[#:preamble preamble bytes? #""])
response?]{
Equivalent to
@racketblock[
(response/full
code message seconds mime-type
(append headers (map cookie->header cookies))
(list preamble (string->bytes/utf-8 (xexpr->string xexpr))))
]}
}

View File

@ -49,7 +49,8 @@ An example @racket['stateless] servlet module:
serialize-stuffer
(md5-stuffer (build-path (find-system-path 'home-dir) ".urls"))))
(define (start req)
`(html (body (h2 "Look ma, no state!"))))
(response/xexpr
`(html (body (h2 "Look ma, no state!")))))
]

View File

@ -10,6 +10,7 @@
web-server/private/dispatch-server-unit
web-server/private/dispatch-server-sig
web-server/dispatchers/dispatch
net/tcp-sig
racket/async-channel
unstable/contract
web-server/configuration/configuration-table)

View File

@ -2,7 +2,8 @@
@(require "web-server.rkt")
@title[#:tag "mime-types"]{MIME Types}
@(require (for-label web-server/private/mime-types))
@(require (for-label web-server/private/mime-types
racket/contract))
@defmodule[web-server/private/mime-types]{
@ -10,7 +11,7 @@ This module provides function for dealing with @filepath{mime.types}
files.
@defproc[(read-mime-types [p path-string?])
(hash-table/c symbol? bytes?)]{
(hash/c symbol? bytes?)]{
Reads the @filepath{mime.types} file from @racket[p] and constructs a
hash table mapping extensions to MIME types.
}

View File

@ -30,13 +30,14 @@ When used inside @racket[page] syntactically, a rename transformer for the proce
A simple example:
@racketblock[
(page
`(html
(body
(a ([href
,(embed/url
(λ (req)
"You clicked!"))])
"Click me"))))]
(response/xexpr
`(html
(body
(a ([href
,(embed/url
(λ (req)
"You clicked!"))])
"Click me")))))]
Similarly, many Web applications make use almost exclusively of functions that are arguments to @racket[embed/url] and immediately invoke @racket[send/suspend/dispatch].

View File

@ -9,6 +9,7 @@ There are a number of ways to run Web servlets.
@section[#:tag "insta"]{Instant Servlets}
@(require (for-label (only-in web-server/insta/insta
no-web-browser static-files-path)
web-server/http
web-server/servlet-env))
@defmodulelang[web-server/insta #:use-sources (web-server/insta/insta)]
@ -19,9 +20,10 @@ The fastest way to get a servlet running in the Web server is to use the
@racketmod[
web-server/insta
(define (start request)
`(html (head (title "Hello world!"))
(body (p "Hey out there!"))))
(define (start req)
(response/xexpr
`(html (head (title "Hello world!"))
(body (p "Hey out there!")))))
]
And press @onscreen["Run"]. A Web browser will open up showing your new servlet.

View File

@ -5,6 +5,7 @@
@(require (for-label web-server/servlet-env
web-server/servlet-dispatch
web-server/http
web-server/managers/manager
web-server/managers/lru
web-server/private/util
web-server/dispatchers/dispatch

View File

@ -28,9 +28,10 @@ racket
(require web-server/servlet
web-server/servlet-env)
(define (start request)
`(html (head (title "Hello world!"))
(body (p "Hey out there!"))))
(define (start req)
(response/xexpr
`(html (head (title "Hello world!"))
(body (p "Hey out there!")))))
(serve/servlet start)
]
@ -41,9 +42,10 @@ racket
(require web-server/servlet
web-server/servlet-env)
(define (my-app request)
`(html (head (title "Hello world!"))
(body (p "Hey out there!"))))
(define (my-app req)
(response/xexpr
`(html (head (title "Hello world!"))
(body (p "Hey out there!")))))
(serve/servlet my-app)
]
@ -117,7 +119,8 @@ You can also put the call to @racket[serve/servlet] in the @racketmodname[web-se
(start
(send/suspend
(lambda (k-url)
`(html (body (a ([href ,k-url]) "Hello world!")))))))
(response/xexpr
`(html (body (a ([href ,k-url]) "Hello world!"))))))))
(serve/servlet start #:stateless? #t)
]

View File

@ -6,7 +6,9 @@
web-server/http
web-server/private/servlet
web-server/managers/manager
web-server/configuration/namespace))
web-server/configuration/namespace
racket/serialize
web-server/stuffers/stuffer))
@defmodule[web-server/servlet/setup]{

View File

@ -39,11 +39,13 @@ An example version 2 module:
(define manager
(create-none-manager
(lambda (req)
`(html (head (title "No Continuations Here!"))
(body (h1 "No Continuations Here!"))))))
(response/xexpr
`(html (head (title "No Continuations Here!"))
(body (h1 "No Continuations Here!")))))))
(define (start req)
`(html (head (title "Hello World!"))
(body (h1 "Hi Mom!"))))
(response/xexpr
`(html (head (title "Hello World!"))
(body (h1 "Hi Mom!")))))
]
These servlets have an extensive API available to them: @racketmodname[net/url], @racketmodname[web-server/http],

View File

@ -1,6 +1,7 @@
#lang scribble/doc
@(require "web-server.rkt"
(for-label web-server/lang/soft
(for-label web-server/http
web-server/lang/soft
web-server/lang/web))
@title[]{Soft State}
@ -47,7 +48,8 @@ Here's an example servlet that uses soft state:
(start
(send/suspend
(lambda (k-url)
`(html (body (a ([href ,k-url]) "Done")))))))
(response/xexpr
`(html (body (a ([href ,k-url]) "Done"))))))))
]
When this is run and the link is clicked a few times, the output is:

View File

@ -256,18 +256,13 @@ Notice how it also avoids the absurd amount of punctuation on line two.
@section{HTTP Responses}
The quickest way to generate an HTTP response from a template is using
the @racket[list] response type:
@racketblock[
(list #"text/html" (include-template "static.html"))
]
If you want more control then you can generate a @racket[response?] struct:
a @racket[response?] struct:
@racketblock[
(response/full
200 #"Okay"
(current-seconds) TEXT/HTML-MIME-TYPE
empty
(list (include-template "static.html")))
(list (string->bytes/utf-8 (include-template "static.html"))))
]
Finally, if you want to include the contents of a template inside a larger @xexpr :
@ -335,12 +330,13 @@ He has divided his code into presentation functions and logic functions. We'll l
The first presentation function defines the common layout of all pages.
@racketblock[
(define (template section body)
`(html
(head (title "Al's Church: " ,section))
(body
(h1 "Al's Church: " ,section)
(div ([id "main"])
,@body))))
(response/xexpr
`(html
(head (title "Al's Church: " ,section))
(body
(h1 "Al's Church: " ,section)
(div ([id "main"])
,@body)))))
]
One of the things to notice here is the @racket[unquote-splicing] on the @racket[body] argument.
@ -379,32 +375,33 @@ his blog. He changes the @racket[template] function to:
@racketblock[
(define (template section body)
`(html
(head
(title "Al's Church: " ,section)
(style ([type "text/css"])
"body {margin: 0px; padding: 10px;}"
"#main {background: #dddddd;}"))
(body
(script
([type "text/javascript"])
,(make-cdata
#f #f
"var gaJsHost = ((\"https:\" =="
"document.location.protocol)"
"? \"https://ssl.\" : \"http://www.\");"
"document.write(unescape(\"%3Cscript src='\" + gaJsHost"
"+ \"google-analytics.com/ga.js' "
"type='text/javascript'%3E%3C/script%3E\"));"))
(script
([type "text/javascript"])
,(make-cdata
#f #f
"var pageTracker = _gat._getTracker(\"UA-YYYYYYY-Y\");"
"pageTracker._trackPageview();"))
(h1 "Al's Church: " ,section)
(div ([id "main"])
,@body))))
(response/xexpr
`(html
(head
(title "Al's Church: " ,section)
(style ([type "text/css"])
"body {margin: 0px; padding: 10px;}"
"#main {background: #dddddd;}"))
(body
(script
([type "text/javascript"])
,(make-cdata
#f #f
"var gaJsHost = ((\"https:\" =="
"document.location.protocol)"
"? \"https://ssl.\" : \"http://www.\");"
"document.write(unescape(\"%3Cscript src='\" + gaJsHost"
"+ \"google-analytics.com/ga.js' "
"type='text/javascript'%3E%3C/script%3E\"));"))
(script
([type "text/javascript"])
,(make-cdata
#f #f
"var pageTracker = _gat._getTracker(\"UA-YYYYYYY-Y\");"
"pageTracker._trackPageview();"))
(h1 "Al's Church: " ,section)
(div ([id "main"])
,@body)))))
]
@margin-note{Some of these problems go away by using here strings, as described in the documentation on
@ -447,8 +444,11 @@ To use templates, we need only change @racket[template], @racket[blog-posted], a
@racketblock[
(define (template section body)
(list TEXT/HTML-MIME-TYPE
(include-template "blog.html")))
(response/full
200 #"Okay"
(current-seconds) TEXT/HTML-MIME-TYPE
empty
(list (string->bytes/utf-8 (include-template "blog.html")))))
(define (blog-posted title body k-url)
(include-template "blog-posted.html"))

View File

@ -2,7 +2,8 @@
@(require "web-server.rkt")
@title[#:tag "web-cells"]{Web Cells}
@(require (for-label web-server/servlet/web-cells
@(require (for-label web-server/http
web-server/servlet/web-cells
web-server/servlet/web))
@defmodule[web-server/servlet/web-cells]{The
@ -56,12 +57,13 @@ transformations of the program into continuation or store passing style.
(define include2 (include-counter counter2))
(send/suspend/dispatch
(lambda (embed/url)
`(html
(body (h2 "Double Counters")
(div (h3 "First")
,(include1 embed/url))
(div (h3 "Second")
,(include2 embed/url)))))))
(response/xexpr
`(html
(body (h2 "Double Counters")
(div (h3 "First")
,(include1 embed/url))
(div (h3 "Second")
,(include2 embed/url))))))))
(define (make-counter)
(make-web-cell 0))

View File

@ -19,11 +19,12 @@ functions of interest for the servlet developer.
Example:
@racketblock[
(send/back
`(html
(body
(h1 "The sum is: "
,(+ first-number
second-number)))))
(response/xexpr
`(html
(body
(h1 "The sum is: "
,(+ first-number
second-number))))))
]
}
@ -39,12 +40,13 @@ functions of interest for the servlet developer.
@racketblock[
(send/suspend
(lambda (k-url)
`(html (head (title "Enter a number"))
(body
(form ([action ,k-url])
"Enter a number: "
(input ([name "number"]))
(input ([type "submit"])))))))
(response/xexpr
`(html (head (title "Enter a number"))
(body
(form ([action ,k-url])
"Enter a number: "
(input ([name "number"]))
(input ([type "submit"]))))))))
]
When this form is submitted by the browser, the request will be sent to the URL generated by @racket[send/suspend].
@ -78,20 +80,21 @@ functions of interest for the servlet developer.
(count-dot-com
(send/suspend/dispatch
(lambda (embed/url)
`(html
(head (title "Count!"))
(body
(h2 (a ([href
,(embed/url
(lambda (req)
(sub1 i)))])
"-"))
(h1 ,(number->string i))
(h2 (a ([href
,(embed/url
(lambda (req)
(add1 i)))])
"+"))))))))
(response/xexpr
`(html
(head (title "Count!"))
(body
(h2 (a ([href
,(embed/url
(lambda (req)
(sub1 i)))])
"-"))
(h1 ,(number->string i))
(h2 (a ([href
,(embed/url
(lambda (req)
(add1 i)))])
"+")))))))))
]
Notice that in this example the result of the handlers are returned to the continuation of @racket[send/suspend/dispatch].
However, it is very common that the return value of @racket[send/suspend/dispatch] is irrevelant in
@ -100,20 +103,21 @@ functions of interest for the servlet developer.
(define (count-dot-com i)
(send/suspend/dispatch
(lambda (embed/url)
`(html
(head (title "Count!"))
(body
(h2 (a ([href
,(embed/url
(lambda (req)
(count-dot-com (sub1 i))))])
"-"))
(h1 ,(number->string i))
(h2 (a ([href
,(embed/url
(lambda (req)
(count-dot-com (add1 i))))])
"+")))))))
(response/xexpr
`(html
(head (title "Count!"))
(body
(h2 (a ([href
,(embed/url
(lambda (req)
(count-dot-com (sub1 i))))])
"-"))
(h1 ,(number->string i))
(h2 (a ([href
,(embed/url
(lambda (req)
(count-dot-com (add1 i))))])
"+"))))))))
]
}
@ -136,9 +140,10 @@ functions of interest for the servlet developer.
Use this if the user is truly `done' with your application. For example, it may be used to display the post-logout page:
@racketblock[
(send/finish
`(html (head (title "Logged out"))
(body (p "Thank you for using the services "
"of the Add Two Numbers, Inc."))))
(response/xexpr
`(html (head (title "Logged out"))
(body (p "Thank you for using the services "
"of the Add Two Numbers, Inc.")))))
]
}
@ -164,7 +169,8 @@ functions of interest for the servlet developer.
(parameterize
([current-servlet-continuation-expiration-handler
(lambda (req)
`(html (head (title "Custom Expiration!"))))])
(response/xexpr
`(html (head (title "Custom Expiration!")))))])
(send/suspend
....))
]

View File

@ -1,4 +1,4 @@
#lang racket
#lang racket/base
(require xml
scribble/text
racket/port)