Bindings associated with the request returned by the
send-*/(suspend|forward)
functions, or passed
as parameters to procedures that are either published or the
target of a dispatch, can be extracted and associated to
language-level variables.
Bindings, as returned by the function
get-bindings
are contained in an opaque
object. This object can be converted to an association list via
bindings->alist
, or queried in constant time
using the extract-bindings
or
extract-single-binding
functions. The object
is guaranteed to be serializable, but code should not rely on its
specific implementation.
Requires:
(import siscweb/bindings)
Located in:
siscweb.jar
This module assists developers in obtaining values of parameters passed in a request.
procedure:
(get-bindings [char-set] request) => bindings
Given a request object, this procedure returns an opaque object representing the request parameters as key-value(s) pairs.
The optional
char-set
parameter can be used to override the character encoding of the request.
procedure:
(exists-binding? name bindings) => #t/#f
Returns a boolean indicating whether a parameter with the given
name
contained in the request object.name
can be either a string or a symbol.
procedure:
(extract-single-binding name bindings) => string/#f
Returns the value associated to the given
name
or #f ifname
is not present in the request object. If more than one value is bound toname
, only the first one is returned.
procedure:
(extract-bindings name bindings) => list
Returns the values bound to the given
name
in a list. Even if only one value is bound to the request parametername
, a list will still be returned.
procedure:
(bindings->alist bindings) => ((name . value) ...)
Returns an association list corresponding to the bindings. The
name
returned is always a string to maintain its casing, while thevalue
returned is an atom or a list ifname
is associated to multiple values.
syntax:
(let-bindings ((variable name) ... ) bindings body ...)) => value
Behaves like (let ...), but
variable
s are set to the values corresponding to thename
s in thebindings
object. Thevalue
returned is that of the last expression in thebody
. A typical usage scenario could be:(let-bindings ((name "name")) (get-bindings (send-html/suspend (lambda (k-url) `(html (body (form (@ (action ,k-url)) (input (@ (type "text") (name "name"))))))))) (send-html/back `(html (body (p "You typed: " ,name)))))
Bindings specified in the send-forward/*
functions are assigned to request attributes. This method
supercedes the previous request.getBinding*()
API, which is now deprecated and will be removed in the
next release.
Multi-valued bindings, as in '((messages "hello"
"there"))
, are turned into a
java.util.List
and can be easily scanned
using such tools as JSTL's
<c:forEach>
. Single-valued bindings,
such as '((message . "hello"))
are simply
assigned to the attribute.
Multi-valued bindings originating from a Scheme list are also
marked as such, so that they can be converted back to a list of
values rather than to an object of type
java.util.List
.
A minimal amount of type conversion is performed. Scheme values are passed as SISC objects, except for Scheme strings, which are converted to Java strings. Java objects are left untouched. This also applies to the individual values of multi-valued bindings.
For instance, below is the Polyglot Hello World example using JSP/JSTL as the presentation layer. This example is not included in the distribution because of its external dependencies on the JSTL libraries.
;; file: hello.scm (require-library 'siscweb/forward) (module examples/hello-world (hello-world) (import siscweb/forward) (define messages '("Hello, world!" "Salve, mundo!" "Hallo, Welt!" "Salve, mondo!" "Bonjour, monde!" "Hola, mundo!")) (define (hello-world req) (let loop () (for-each (lambda (message) (send-forward/suspend "/jsp/hello.jsp" `((message . ,message)))) messages) (loop))) ) <%-- File: jsp/hello.jsp --%><%@ page contentType="text/html" %><%-- --%><%@ page isELIgnored="false" %><%-- just for servlet v2.3 containers --%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%-- --%><%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml"%><%-- --%><%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%><%-- --%><%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql"%><%-- --%><%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%><%-- --%><?xml version="1.0"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title><c:out value="${requestScope.message}"/></title> <c:url var="cssUrl" value="/css/default.css"/> <link href="${cssUrl}" rel="stylesheet" type="text/css"/> </head> <body> <h3>Polyglot hello world</h3> <p>${requestScope.message}</p> <p><a href="${requestScope["siscweb.kURL"]}">Next language ></a></p> <p><a href="${requestScope["siscweb.kURL"]}" target="_blank">Next language in new window</a></p> <c:url var="homeUrl" value="/"/> <p><a href="${homeUrl}">^ Home</a></p> </body> </html>
At the lower level, bindings are looked up in the request scope first (i.e. from request attributes) and then from request parameters. This behavior allows a more natural integration with standard J2EE components such as JSPs, but can be unsafe during forwards.
Specifically, if the application performs a server-side forward to Scheme code that relies on the absence of a binding to determine a course of action, a malicious user can inject a parameter by the same name into the request (provided they can guess the name), and upset the application's behavior. The best options in this case are:
When forwarding from Scheme, pass bindings to the
forward/*
and
send-forward/*
functions even if they
are #f or the empty list. SISCweb will add the name to a
per-request blacklist, and will not look up the binding
among the request parameters.
When forwarding from Java to Scheme, set the attributes to
null
. Again, the name will be
blacklisted.
If the Java code that forwards to the Scheme code cannot be
trusted or modified for some reason, the Scheme side can
always use the java/get-request-attribute
function described in the section called “Request Procedures”.