On this page:
5.1 Using web-server/  dispatch
5.2 API Reference
dispatch-rules
dispatch-rules+  applies
dispatch-case
dispatch-url
serve/  dispatch
5.3 Imperative Dispatch Containers
container?
define-container
dispatch-rules!
5.4 Built-in URL patterns
number-arg
integer-arg
real-arg
string-arg
symbol-arg
5.5 Extending web-server/  dispatch
define-bidi-match-expander
bidi-match-going-in?
define-coercion-match-expander
make-coerce-safe?

5 URL-Based Dispatch

 (require web-server/dispatch)

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]))

And define your request handlers as follows:
> (define (list-posts req) `(list-posts))
> (define (review-post req p) `(review-post ,p))
> (define (review-archive req y m) `(review-archive ,y ,m))

Now when a request is sent to your application, it will be directed to the appropriate handler:
> (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)

You can also generate these pretty URLs from procedure calls:
> (blog-url list-posts)

"/"

> (blog-url review-post "Another-Saturday-Night")

"/posts/Another-Saturday-Night"

> (blog-url review-archive 1984 11)

"/archive/1984/11"

After mastering the world of blogging software, you decide to put the ubiquitous Add-Two-Numbers.com out of business with Sum.com:
> (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)
Returns two values: the first is a dispatching function with the contract (-> request? any) that calls the appropriate dispatch-fun based on the first dispatch-pattern that matches the request’s URL (and method), the second is a URL-generating function with the contract (-> procedure? any/c ... string?) that generates a URL using dispatch-pattern for the dispatch-fun given as its first argument.

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)
Like dispatch-rules, except returns a third value with the contract (-> request? boolean?) that returns #t if the dispatching rules apply to the request and #f otherwise.

syntax

(dispatch-case
 dispatch-clause ...
 maybe-else-clause)
Returns a dispatching function as described by dispatch-rules.

syntax

(dispatch-url
 [dispatch-pattern dispatch-fun]
 ...)
 
  dispatch-fun : (request? any/c ... . -> . any)
Returns a URL-generating function as described by dispatch-rules.

procedure

(serve/dispatch dispatch)  void

  dispatch : (request? . -> . can-be-response?)
Calls serve/servlet with a #:servlet-regexp argument (#rx"") so that every request is handled by dispatch.

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
Identifies containers.

syntax

(define-container container-id (dispatch-id url-id))

Defines container-id as a container as well as dispatch-id as its dispatching function and url-id as its URL lookup function.

syntax

(dispatch-rules! container-expr [dispatch-pattern dispatch-fun] ...)

Like dispatch-rules, but imperatively adds the patterns to the container specified by container-expr. The new rules are consulted before any rules already in the container.

5.4 Built-in URL patterns

web-server/dispatch builds in a few useful URL component patterns.

syntax

(number-arg)

A bi-directional match expander that parses a number? from the URL and generates a URL with a number’s encoding as a string.

syntax

(integer-arg)

A bi-directional match expander that parses a integer? from the URL and generates a URL with a integer’s encoding as a string.

syntax

(real-arg)

A bi-directional match expander that parses a real? from the URL and generates a URL with a real’s encoding as a string.

syntax

(string-arg)

A bi-directional match expander that parses a string? from the URL and generates a URL containing the string.

syntax

(symbol-arg)

A bi-directional match expander that parses a symbol? from the URL and generates a URL with a symbol’s encoding as a string.

5.5 Extending web-server/dispatch

 (require web-server/dispatch/extend)

You can create new URL component patterns by defining bi-directional match expanders.

syntax

(define-bidi-match-expander id in-xform out-xform)

Binds id to a bi-directional match expander where in-xform is a match expander (defined by define-match-expander) that is used when parsing URLs and out-xform is one used when generating URLs.

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.

A syntax parameter used by bi-directional match expanders to determine if a URL is being parsed or generated.

When defining new patterns, you may find it useful to use these helper functions:

syntax

(define-coercion-match-expander id test? coerce)

Binds id to a match expander that expands (id x) to (? test? (app coerce x)) (i.e., uses test? to determine if the pattern matches and coerce to transform the binding.)

procedure

(make-coerce-safe? coerce)  (any/c . -> . boolean?)

  coerce : (any/c . -> . any/c)
Returns a function that returns #t if coerce would not throw an exception or return #f on its input.

Examples:

> (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