You are looking at historical revision 19047 of this page. It may differ significantly from its current revision.

spiffy-request-vars

Introduction

spiffy-request-vars provides easy access to variables from HTTP requests.

Author

Mario Domenech Goulart

Thanks to Moritz Heidkamp for the implementation of the content body reader and for several terminology suggestions (including the egg name). Also thanks to Peter Bex for the helpful discussions and lots of implementation suggestions.

Procedures parameters and macros

[procedure] (request-vars #!key (source 'both) max-content-length)

request-vars returns a procedure which can be used to access variables from the HTTP request. The returned procedure takes the name of the variable (either a symbol or a string) as argument. You can (optionally) also pass a converter procedure to be used as a type converter for the variable value (see Converter procedures) or a default value.

request-vars accepts some keyword arguments:

source
'query-string tells request-vars to parse the query string only (for GET variables). 'request-body tells request-vars to parse the request body only (e.g., for POST variables). 'both tells request-vars to parse both the request body and the query string. The default value for source is 'both. Notice that when 'both is used, variables from the request body have precedence over the ones from the query string.
max-content-length
the maximum content length (in characters) to be read from the request body. Default is #f (no limit).

Converter procedures

The following procedures are intended to be used by the procedure returned by request-vars. The variables/values parameter is an alist mapping variable names to their corresponding values, resulting from parsing the request.

[procedure] (as-number variable variables/values)

If the given variable is set, convert its value to a number using string->number.

[procedure] (as-boolean variable variables/values)

If the variable is set and its value is one of the values yield by the true-boolean-values parameter, return #t, otherwise return #f.

[procedure] (as-list variable variables/values)

If the variable is set once, returns a list with a single element (the value for the given variable). If the variable is set multiple times, return a list with the multiple values for the variable. If the variable is not set, return #f.

[procedure] (as-alist variable variables/values)

Returns an alist represented by the request variables/values for the given variable. The request representation for alists is variable.key=value. Example: foo.a=1 would result in '((a . "1")) for the foo variable.

Example:

;; considering a http://server:port/path?foo.a=0&foo.b=1 request

(let (($ (request-vars)))
  ($ 'foo as-alist))   ;; => ((a . "0") (b . "1"))

as-alist returns #f when the wanted variable is not sent in the request or it is sent not in the dot notation (e.g., foo=0).

[procedure] (as-hash-table variable variables/values)

The same as as-alist, but returns a hash-table object instead of an alist.

[procedure] (as-vector variable variables/values)

Returns a vectir represented by the request variables/values for the given variable. The request representation for vectors is variable.numeric-index=value. Example: foo.0=1 would result in #("1") for the foo variable.

Example:

;; considering a http://server:port/path?foo.0=a&foo.1=b

(let (($ (request-vars)))
  ($ 'foo as-vector))   ;; => #("a" "b")

as-vector returns #f when the wanted variable is not sent in the request or it is sent not in the dot notation (e.g., foo=0).

If the vector represented by the request is sparse, the missing items are unspecified values.

[parameter] (true-boolean-values)

A list of values (strings) to be considered as #t for request variables when as-boolean is used as converter.

The default value is '("y" "yes" "1" "on"). The values are compared using string-ci=?.

Example

  (let (($ (request-vars)))
    ($ 'var1)
    ($ 'var2 "") ;; if var12 is not set, return ""
    ($ 'var3 as-number)) ;; if var3 is not set, return #f; if it is
                         ;; set, convert its value to a number
[syntax] (with-request-vars [getter] (var1 var2 ... varN) expr1 expr2 ... exprN)

Bind the given identifiers to the corresponding query string and request body variable values and evaluate the expressions. The optional getter argument (the return value of request-vars) may be used in situations when you already have the getter and don't want to reparse the query string and request body. With with-request-vars*, the given getter will be used and no reparsing will be performed. When the syntax is ambiguous (e.g., (with-request-vars (request-vars) (var1 var2) (body)), with-request-vars* can be used).

Examples

(with-request-vars (a b c)
   (conc "a = " a
         "b = " b
         "c = " c))
(let (($ (request-vars)))
  (with-request-vars $ (a b c)
     (conc "a = " a
           "b = " b
           "c = " c)))

A converter procedure can also be used to specify the type of the variable values:

(let (($ (request-vars)))
  (with-request-vars $ (a (b as-list) (c as-number))
     (conc "a = " a
           "b = " b
           "c = " c)))
[syntax] (with-request-vars* getter (var1 var2 ... varN) expr1 expr2 ... exprN)

The same as with-request-vars, but the getter is mandatory.

More examples

Considering

(define $ (request-vars))

here are some expected results for the given requests:

;; http://host:port/

($ 'foo)             => #f
($ 'foo 'bar)        => bar
($ 'foo as-list)     => #f
($ 'foo as-boolean)  => #f
($ 'foo as-number)   => #f


;; http://host:port/?foo=bar

($ 'foo)             => "bar"
($ 'foo 'bar)        => "bar"
($ 'foo as-list)     => ("bar")
($ 'foo as-boolean)  => #f
($ 'foo as-number)   => #f


;; http://host:port/?foo=bar&foo=baz

($ 'foo)             => "bar"
($ 'foo 'bar)        => "bar"
($ 'foo as-list)     => ("bar" "baz")
($ 'foo as-boolean)  => #f
($ 'foo as-number)   => #f


;; http://host:port/?foo=0

($ 'foo)             => "0"
($ 'foo 'bar)        => "0"
($ 'foo as-list)     => ("0")
($ 'foo as-boolean)  => #f
($ 'foo as-number)   => 0


;; http://host:port/?foo=yes

($ 'foo)             => "yes"
($ 'foo 'bar)        => "yes"
($ 'foo as-list)     => ("yes")
($ 'foo as-boolean)  => #t
($ 'foo as-number)   => #f


;; http://host:port/

(with-request-vars (foo (bar as-list) (baz 5))
  (list foo bar baz) => (#f #f 5)


;; http://host:port/?foo=10

(with-request-vars (foo (bar as-list) (baz 5))
  (list foo bar baz) => ("10" #f 5)


;; http://host:port/?foo=10&bar=1

(with-request-vars (foo (bar as-list) (baz 5))
  (list foo bar baz) => ("10" ("1") 5)


;; http://host:port/?foo=10&bar=1&bar=2

(with-request-vars (foo (bar as-list) (baz 5))
  (list foo bar baz) => ("10" ("1" "2") 5)


;; http://host:port/?foo=10&bar=1&bar=2&baz=-8

(with-request-vars (foo (bar as-list) (baz 5))
  (list foo bar baz) => ("10" ("1" "2") "-8")


;; http://host:port

(with-request-vars ((foo as-alist) (bar as-number) (baz as-vector) (bool as-boolean))
  (list foo bar baz bool)) => (#f #f #f #f)


;; http://host:port/?foo.A=0&foo.B=1&bar=0&baz.0=a&baz.1=b&bool=yes

(with-request-vars ((foo as-alist) (bar as-number) (baz as-vector) (bool as-boolean))
  (list foo bar baz bool)) => (((A . "0") (B . "1")) 0 #("a" "b") #t)


;; http://host:port/?foo=0&bar=a&baz=0&bool=3

(with-request-vars ((foo as-alist) (bar as-number) (baz as-vector) (bool as-boolean))
  (list foo bar baz bool)) => (#f #f #f #f)

Tips and tricks

If you want to specify both converters and default values, you can use the following trick:


;; Define a procedure to return the default value if the
;; variable is not set.
(define ((as-number/default default) var vars/vals)
  (or (as-number var vars/vals) default))


;; http://host:port/

(with-request-vars (foo (bar as-list) (baz (as-number/default 3)))
  (->string (list foo bar baz))) => (#f #f 3)


;; http://host:port/?baz=9

(with-request-vars (foo (bar as-list) (baz (as-number/default 3)))
  (->string (list foo bar baz))) => (#f #f 9)

License

BSD

Requirements

Version history

0.10
bug fix for some corner cases
0.9
added as-vector, as-alist and as-hash-table as converters.
0.8
bug fix/improvement: don't bother reading the request body when the content-length is zero (fixes some awful/jquery ajax issues)
0.7
bug fix. Interpret request body before query string.
0.6
0.5
with-request-vars accepts a getter as argument when the syntax is not ambiguous.
0.4
with-request-vars* resurrected. For the cases when the syntax of with-request-vars is ambiguous. Thanks to Moritz Heidkamp for catching this bug.
0.3
Removed with-request-vars*. with-request-vars accepts an optional getter argument
0.2
Added with-request-vars and with-request-vars*
0.1
Initial release