
Application/Middleware boilerplates
CGI parameter parsing and retrieval
Getting environment parameters by relativized names
Locating and calling a template/application by URL
Parse and format JSON data
Evaluate a parameter
Delegate to a parameter
Delegate to a URL
Getting and Putting WHIFF registered resources
When WHIFF selects a module to respond to a request WHIFF looks for
a function in the module named __wsgi__ or a class (constructor
callable) in the module named __middleware__. WHIFF will not
automatically directly use other entries in the module as a security
precaution.
For the purposes of this discussion we will distinguish two two main categories of applications: generators create response to a web request themself, and delegators which delegate the generation of the web response to another application, usually after modifying the execution environment or configuring the other application. A couple examples taken from the standard middleware directory will illustrate the difference between generators and delegators, as well as provide example of most of the API functionality offered by WHIFF to Python based WSGI applications.
The standard middleware DebugDump is a generator which dumps the application environment dictionary as the response to the web request.
# content of whiff/middleware/debugDump.py
from whiff.middleware import misc
from whiff import resolver
from whiff.rdjson import jsonParse
from whiff.middleware import quoteHtml
class dump(misc.utility):
"dump out the environment as text/plain with html special characters quoted"
def __init__(self,
parse_cgi = True,
content_type='text/plain'):
self.content_type = content_type
self.parse_cgi = parse_cgi
def __call__(self, env, start_response):
# determine the content type desired for the reply
content_type = self.param_value(self.content_type, env)
# determine whether to parse the CGI parameters
parse_cgi = self.param_json(self.parse_cgi, env)
if parse_cgi:
# parse CGI parameters, as requested
env = resolver.process_cgi(env, parse_cgi=True)
# format the env dictionary as a JSON string representation
json_env = jsonParse.format(env)
# "quote" HTML special characters
quoted_json = resolver.quote(json_env)
# start the response
start_response("200 OK", [('Content-Type', content_type)])
# deliver the response
return [quoted_json]
__middleware__ = dump
debugDump is built to a code pattern common to many generator implementations,
which looks something like this
from whiff.middleware import misc
... MORE IMPORTS ...
class APPLICATION(misc.utility):
def __init__(self,
...APPLICATION PARAMETERS...
):
...RECORD THE PARAMETERS FOR FUTURE USE..
def __call__(self, env, start_response):
...COMPUTE THE RESPONSE TO THE REQUEST...
start_response("200 OK", [('Content-Type', CONTENT_TYPE FOR REQUEST)])
return RESPONSE ITERABLE
__middleware__ = APPLICATION
APPLICATION
inherits from the whiff.middleware.misc.utility abstract superclass
because the superclass provides valuable conveniences. For example the debugDump
application uses the param_value, param_json and
deliver_page methods inherited from the
whiff.middleware.misc.utility
superclass.
Note also that the assignment
__middleware__ = APPLICATION
The main difference between a generator like DebugDump and a
delegator like ifVisible, shown below, is that a generator starts
the response, decides the content type of the response and explicitly returns
the response iterable (which should be a sequence of string values).
IfVisible does not start the response, nor decide the content type,
nor explicitly create the response, but delegates all of those responsibilities
to one of its parameters, either page or elsePage
depending on whether testPage evaluates to whitespace or not.
# content of whiff/middleware/ifVisible.py
"""
Simple conditional based on page value: test if all white or not
"""
from whiff.middleware import misc
class ifVisible(misc.utility):
def __init__(self,
testPage,
page,
elsePage="", # page to deliver if testpage is all white
):
self.testPage = testPage
self.page = page
self.elsePage = elsePage
def __call__(self, env, start_response):
test = self.param_value(self.testPage, env).strip()
if test:
return self.deliver_page(self.page, env, start_response)
else:
return self.deliver_page(self.elsePage, env, start_response)
__middleware__ = ifVisible
from whiff.middleware import misc
class APPLICATION(misc.utility):
def __init__(self,
OTHER PARAMETERS...
):
...STORE OTHER PARAMETERS FOR FUTURE USE...
def __call__(self, env, start_response):
...DECIDE HOW TO DELEGATE THE RESPONSE...
...AND POSSIBLY CHANGE THE ENVIRONMENT...
return self.deliver_page(OTHER APPLICATION, env, start_response)
__middleware__ = APPLICATION
The following sections describe the WHIFF services which may help you fill in the boilerplate.
The WHIFF utility function
new_env = whiff.resolver.process_cgi(env, parse_cgi=True)
Once CGI parameters have been parsed and stored as a dictionary in the environment the utility function
value = whiff.whiffenv.cgiGet(env, variable_name)
If you want to use CGI parameters that may have multiple values
you will need to directly use the argument dictionary stored in
the environment new_env[whiffenv.CGI_DICTIONARY] which
stores a mapping of variable names to lists of values.
Both the above methods will automatically reflect any CGI prefix
in effect in order to allow relative naming for
page fragments. For example if the CGI prefix in the environment is "myPrefix"
then whiff.whiffenv.cgiGet(env, "MyVariable") will get the
value associated with the globally submitted CGI parameter named
myPrefixMyVariable.
To access the dictionary of all CGI parameters and values, ignoring
any CGI prefix which may be in effect, use the dictionary
new_env[whiffenv.TOP_CGI_DICTIONARY] (in most cases this
is not needed and not a good idea).
In order to make it easy for WHIFF applications to avoid "javascript injection attacks" and other security problems WHIFF delivers the CGI parameters in HTML-quoted form. So if the user typed the value
<script> alert("Purple dinosour"); </script>
<script> alert("Purple dinosour"); </script>
whiff.resolver.unquote function
>>> resolver.unquote('<script> alert("Purple dinosour"); </script>')
'<script> alert("Purple dinosour"); </script>'
env and can directly access any information in the dictionary.
If a CGI prefix is in effect it may be useful for the application to see
only the parameters matching the CGI prefix.
To find the absolute name associated with a relative Name use the utility function
absolute_Name = whiff.whiffenv.getName(env, Name)
{{id Name/}} configuration
directive).
To find the value associated with a relative Name use the utility function
Value = whiff.whiffenv.getId(env, Name)
{{get-id Name/}} configuration directive.)
To add a value to the environment by relative Name use
whiff.whiffenv.setId(env, Name, Value)
callTemplate is designed to provide this functionality
application = whiff.middleware.callTemplate.callTemplate("url/to/application")
callTemplate must be called as a WSGI application
to get the response from the application, for example as in
return application(env, start_response)
{{include ...}} configuration
directive.
data = self.param_json(parameter, env)
true or 100
to an application and for sending complex values such as sequences of mappings.
To convert a Python data structure which is compatible with JSON data restrictions to a JSON representation use the utility function
stringRepresentation = whiff.rdjson.jsonParse.format(dataStructure)
unicodeStringResponse = self.param_value(parameter, env)
quoteHTML middleware
to obtain the value of its parameter before transforming the value by quoting
HTML special characters. In the event that the parameter is expected to be
a binary value such as a PNG image use the param_binary method
binary8bitString = self.param_binary(parameter, env)
deliver_page like this:
return self.deliver_page(other_application, env, start_response)
deliver_page method collaborates with the other misc.utility
methods to make sure that all headers produced by components are included in the
response. In addition, deliver_page permits the other_application
to be a simple string. For example one way to implement the "hello world" WSGI application
in WHIFF is:
from whiff.middleware import misc
class hello(misc.utility):
def __call__(self, env, start_response):
return self.deliver_page("hello_world", env, start_response)
__middleware__ = hello
deliver_page method takes care of the rest of the WSGI protocol automatically.
whiff.resolver.callUrl(url, env, start_response) function
to delegate the response to an application located by an URL. For example the following
application essentially performs an "internal redirect" to a authenticate/login
page after modifying the environment.
from whiff import resolver
def __wsgi__(env, start_response):
env = env.copy()
env["greeting"] = "GREETINGS EARTHLING!"
return resolver.callUrl("authenticate/login", env, start_response)
resolver.callUrl.
whiff.gateway module provides standard interfaces for WHIFF registered resources.
# get a resource (raise whiff.gateway.NoSuchResource if missing)
value = whiff.gateway.getResource(env, resourcePath)
# get a resource (use the defaultValue if missing)
value = whiff.gateway.getResource(env, resourcePath, defaultValue)
# put a resource
whiff.gateway.putResource(env, resourcePath, value)