A Python web framework that makes the most of the
filesystem.
Simplates are the
main attraction.
The Aspen Request object tries to consistently model the actual structure of an HTTP Request message. Consequently, it is rather verbose. There are shortcuts available, however.
An instance of the aspen.http.request.Request class is made
available to you in your simplates from the second
page on. It is also passed to your functions registered with the inbound_early
and inbound_late hooks.
The most common parts of the HTTP Request message are given these shortcuts inside of simplate:
| body | request.body |
| cookie | request.headers.cookie |
| headers | request.headers |
| path | request.line.uri.path |
| qs | request.line.uri.querystring |
For example, here’s how to pull a value out of the querystring:
foo = qs["foo"]
There are also booleans for each of the standard HTTP methods, so simplates in a REST style will end up looking like this:
if GET:
pass # handle GET )
elif POST:
pass # handle POST
There is no API that folds the querystring and body together. It’s better to be explicit about where you’re expecting your values from.
The following object model is used to comprehensively represent an HTTP Request message. Where “raw” attributes are noted those give the bytes as supposedly received on the wire. As an optimization, this attribute is lazily computed for request.body.raw. The request, itself a subclass of str, is also dealt with lazily.
| name | class | base type | notes |
|---|---|---|---|
| request | Request | str | |
| request.line | Line | str | |
| request.line.method | Method | unicode | |
| request.line.method.raw | str | ||
| request.line.uri | URI | unicode | |
| request.line.uri.scheme | UnicodeWithRaw | unicode | |
| request.line.uri.scheme.raw | str | ||
| request.line.uri.username | UnicodeWithRaw | unicode | |
| request.line.uri.username.raw | str | ||
| request.line.uri.password | UnicodeWithRaw | unicode | |
| request.line.uri.password.raw | str | ||
| request.line.uri.host | UnicodeWithRaw | unicode | |
| request.line.uri.host.raw | str | ||
| request.line.uri.port | IntWithRaw | int | 0 if absent |
| request.line.uri.port.raw | str | ||
| request.line.uri.path | Path | Mapping | |
| request.line.uri.path.raw | str | ||
| request.line.uri.path.decoded | unicode | ||
| request.line.uri.querystring | Querystring | Mapping | |
| request.line.uri.querystring.raw | str | ||
| request.line.uri.querystring.decoded | unicode | ||
| request.line.uri.raw | str | ||
| request.line.version | Version | unicode | |
| request.line.version.info | tuple | ||
| request.line.version.major | int | ||
| request.line.version.minor | int | ||
| request.line.version.raw | str | ||
| request.line.raw | str | ||
| request.headers | Headers | ||
| request.headers.cookie | Cookie.SimpleCookie | ||
| request.headers.host | unicode | X-Forwarded-For if it exists, Host otherwise (no Host will raise Response(400)) | |
| request.headers.scheme | unicode | u"https" or u"http" | |
| request.headers.raw | str | ||
| request.body | Body | ||
| request.body.raw | str |
Here are the methods on the Request object:
request.allow(*methods):
"""Given method strings, raise 405 if we don't meet the requirements.
The method names are case insensitive (they are uppercased). If 405
is raised then the Allow header is set to the methods given.
"""
request.is_xhr():
"""Check the value of X-Requested-With.
"""
request.redirect(location, code=None, permanent=False):
"""Given a string, an int, and a boolean, raise a Response.
If code is None then it will be set to 301 (Moved Permanently)
if permanent is True and 302 (Found) if it is False.
"""
The Body, Headers, Path, and
Querystring objects all have the same API. They are asymetric
dictionaries. With HTTP mappings you always need to deal with the “either
list or value” problem. Using them as simple values is much more common
then using them as lists of values, so the base dictionary access set/get
operations are tuned for that, and then you get this API for working with them
as lists of values:
obj.all(name, default=None):
"""Given a name, return a list of values.
"""
obj.add(name, value):
"""Given a name and value, add another entry.
"""
Putting those together, here’s what it looks like:
>>> headers['Content-Type'] = "text/html"
>>> headers['Content-Type'] = "text/plain" # changed my mind!
>>> headers['Content-Type']
text/plain
>>> headers.all('Content-Type')
['text/plain']
Here’s another example:
>>> qs.add('likes', 'cheese!')
>>> qs.add('likes', 'grommit!')
>>> qs['likes']
'grommit!'
>>> qs.all('likes')
['cheese!', 'grommit!']
They’re asymetric in that subscript access respects the underlying list while subscript assignment clobbers it.
If a request is of Content-Type
multipart/form-data, then request.body values that
are file uploads will be instances of the standard library’s cgi.FieldStorage class.
Each such object has filename, value, and
type attributes with the name, bytes, and advertised Content-Type
of the file that was uploaded.