5 URL-Based Dispatch
(require web-server/dispatch) | package: web-server-lib |
The library allows the creation of two-way mappings between permanent URLs and request-handling procedures.
This library was inspired by the (planet untyped/dispatch) package.
5.1 Using web-server/dispatch
Suppose you are writing a blog application and want pretty URLs for different views of the site. You would define some URL dispatching rules as follows:
> (define-values (blog-dispatch blog-url) (dispatch-rules [("") list-posts] [("posts" (string-arg)) review-post] [("archive" (integer-arg) (integer-arg)) review-archive] [else list-posts]))
> (define (list-posts req) `(list-posts))
> (define (review-post req p) `(review-post ,p))
> (define (review-archive req y m) `(review-archive ,y ,m))
> (define (url->request u) (make-request #"GET" (string->url u) empty (delay empty) #f "1.2.3.4" 80 "4.3.2.1"))
> (blog-dispatch (url->request "http://www.chrlsnchrg.com")) '(list-posts)
> (blog-dispatch (url->request "http://www.chrlsnchrg.com/")) '(list-posts)
> (blog-dispatch (url->request "http://www.chrlsnchrg.com/posts/Extracurricular-Activity")) '(review-post "Extracurricular-Activity")
> (blog-dispatch (url->request "http://www.chrlsnchrg.com/archive/1984/10")) '(review-archive 1984 10)
> (blog-dispatch (url->request "http://www.chrlsnchrg.com/contact")) '(list-posts)
> (blog-url list-posts) "/"
> (blog-url review-post "Another-Saturday-Night") "/posts/Another-Saturday-Night"
> (blog-url review-archive 1984 11) "/archive/1984/11"
> (define-values (sum-dispatch sum-url) (dispatch-rules [((integer-arg) ...) sum] [else (lambda (req) (sum req empty))]))
> (define (sum req is) (apply + is))
> (sum-dispatch (url->request "http://www.sum.com/")) 0
> (sum-dispatch (url->request "http://www.sum.com/2")) 2
> (sum-dispatch (url->request "http://www.sum.com/2/3/4")) 9
> (sum-dispatch (url->request "http://www.sum.com/5/10/15/20")) 50
> (sum-url sum empty) "/"
> (sum-url sum (list 1)) "/1"
> (sum-url sum (list 2 3 5 7)) "/2/3/5/7"
When you use web-server/dispatch with serve/servlet, you almost always want to use the #:servlet-regexp argument with the value "" to capture all top-level requests. However, make sure you don’t include an else in your rules if you are also serving static files, or else the filesystem server will never see the requests.
5.2 API Reference
syntax
(dispatch-rules dispatch-clause ... maybe-else-clause)
dispatch-clause = [dispatch-pattern maybe-method dispatch-fun] dispatch-pattern = () | (string . dispatch-pattern) | (bidi-match-expander ... . dispatch-pattern) | (bidi-match-expander . dispatch-pattern) maybe-method =
| #:method method method = pat maybe-else-clause =
| [else else-fun]
else-fun : (request? . -> . any)
dispatch-fun : (request? any/c ... . -> . any)
If else-fun is left out, one is provided that calls (next-dispatcher) to signal to the Web Server that this dispatcher does not apply.
The method syntax is used in a match expression to match the request-method part of the incoming request object. However, since HTTP allows methods to use any case, the byte string from request-method is normalized to a lower-case string. Thus, valid patterns are things like: "get", "post", "head", (or "get" "post"), etc.
If method is left out, it assumed to apply to requests without methods and GET methods.
syntax
(dispatch-rules+applies dispatch-clause ... maybe-else-clause)
syntax
(dispatch-case dispatch-clause ... maybe-else-clause)
syntax
(dispatch-url [dispatch-pattern dispatch-fun] ...)
dispatch-fun : (request? any/c ... . -> . any)
procedure
(serve/dispatch dispatch) → void
dispatch : (request? . -> . can-be-response?)
5.3 Imperative Dispatch Containers
dispatch-rules is purely functional. This presents a more declarative interface, but inhibits some programming and modularity patterns. Containers provide an imperative overlay atop dispatch-rules.
procedure
(container? x) → boolean?
x : any/c
syntax
(define-container container-id (dispatch-id url-id))
syntax
(dispatch-rules! container-expr [dispatch-pattern dispatch-fun] ...)
5.4 Built-in URL patterns
web-server/dispatch builds in a few useful URL component patterns.
syntax
syntax
syntax
(real-arg)
syntax
syntax
5.5 Extending web-server/dispatch
(require web-server/dispatch/extend) | |
package: web-server-lib |
You can create new URL component patterns by defining bi-directional match expanders.
syntax
(define-bidi-match-expander id in-xform out-xform)
Both in-xform and out-xform should use the syntax (xform arg ... id) where the args are specific to id and compatible with both in-xform and out-xform. id will typically be provided automatically by dispatch-rules.
syntax
When defining new patterns, you may find it useful to use these helper functions:
syntax
(define-coercion-match-expander id test? coerce)
> (define string->number? (make-coerce-safe? string->number))
> (string->number? "1") #t
> (string->number? "1.2") #t
> (string->number? "+inf.0") #t
> (string->number? "one") #f