5 URL-Based Dispatch

▼ |  | Web Applications in Racket

1 | Running Web Servlets
2 | Stateful Servlets
3 | Stateless Servlets
4 | HTTP: Hypertext Transfer Protocol
5 | URL-Based Dispatch
6 | Formlets: Functional Form Abstraction
7 | Templates: Separation of View
8 | Page: Short-hand for Common Patterns
9 | Testing Servlets
10 | Troubleshooting and Tips
| Index

► | 5 | URL-Based Dispatch

5.1 | Using web-server/ dispatch
5.2 | API Reference
5.3 | Imperative Dispatch Containers
5.4 | Built-in URL patterns
5.5 | Extending web-server/ dispatch

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?

top ← prev up next →

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

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

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.

> syntax bidi-match-going-in?

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

top ← prev up next →