The Response Class¶
A response object encapsulates the output from a view to the web browser, including:
HTTP status (eg
200 OK
or404 Not Found
)HTTP headers (eg
Content-Type: text/plain
)The response content (eg an html page)
Constructing response objects¶
At the minimum the Response
constructor needs the
response content. If this is all you specify, a successful (200 OK
)
response of type text/html
will be generated with your specified content.
from fresco import Response
def app():
return Response(['<html><body><h1>Hello World</body></html>'])
This will output:
200 OK
Content-Type: text/html; charset=UTF-8
<html><body><h1>Hello World</body></html>
The content argument must be an iterable object – eg a list, a generator expression or any python object that implements the iteration interface)
HTTP headers and a status code can also be supplied. Here’s a longer example, showing more options:
def app():
return Response(
status=405, # method not allowed
content_type='text/html',
allow=['GET', 'POST'],
content=['<html><body>Sorry, that method is not allowed</body></html>']
)
This will output:
405 Method Not Allowed
Allow: GET
Allow: POST
Content-Type: text/html
<html><body>Sorry, that method is not allowed</body></html>
Headers can be supplied as a list of tuples (the same way the WSGI
start_response
function expects them), or as keyword arguments, or any
mixture of the two:
Response(
['<html><body>Sorry, that method is not allowed</body></html>'],
status=405,
headers=[('Content-Type', 'text/html'),
('Allow', 'GET'),
('Allow', 'POST')],
)
Response(
['<html><body>Sorry, that method is not allowed</body></html>'],
status=405,
content_type='text/html',
allow=['GET', 'POST'],
)
Changing response objects¶
Response objects have a range of methods allowing you to add, remove and replace the headers and content. This makes it easy to chain view functions together, each operating on the output of the last:
def view1():
return Response([b"Ten green bottles, hanging on the wall"],
content_type='text/plain')
def view2():
response = view1()
return response.replace(content=[chunk.replace(b'Ten', b'Nine')
for chunk in response.content])
def view3():
response = view2()
return response.replace(content_type='text/html')
>>> view1().content_type
'text/plain'
>>> view1().content
[b'Ten green bottles, hanging on the wall']
>>> view2().content
[b'Nine green bottles, hanging on the wall']
>>> view3().content_type
'text/html'
>>> view3().content
[b'Nine green bottles, hanging on the wall']
Headers may be added, either singly:
>>> r = Response(content=['Whoa nelly!'])
>>> r.headers
[('Content-Type', 'text/html; charset=UTF-8')]
>>> r = r.add_header('Cache-Control', 'private')
>>> sorted(r.headers)
[('Cache-Control', 'private'), ('Content-Type', 'text/html; charset=UTF-8')]
or in groups:
>>> r = Response(content=['Whoa nelly!'])
>>> r.headers
[('Content-Type', 'text/html; charset=UTF-8')]
>>> r = r.add_headers([('Content-Length', '11'), ('Cache-Control', 'Private')])
>>> sorted(r.headers)
[('Cache-Control', 'Private'), ('Content-Length', '11'), ('Content-Type', 'text/html; charset=UTF-8')]
>>> r = r.add_headers(x_powered_by='fresco')
>>> sorted(r.headers)
[('Cache-Control', 'Private'), ('Content-Length', '11'), ('Content-Type', 'text/html; charset=UTF-8'), ('X-Powered-By', 'fresco')]
Removing and replacing headers is the same. See the API documentation for Response
for details.
Integrating with WSGI¶
It’s often useful to be able to switch between Fresco view functions and WSGI application functions – for example, when writing WSGI middleware.
To aid this, Response
objects are themselves WSGI
applications:
>>> def mywsgi_app(environ, start_response):
... r = Response(content=['Whoa nelly!'])
... return r(environ, start_response)
...
>>> print(TestApp(mywsgi_app).get('/').text())
200 OK
Content-Type: text/html; charset=UTF-8
Whoa nelly!
It is also possible to proxy a WSGI application through a Response object, capturing its output to allow further inspection and modification:
>>> def basic_wsgi_app(environ, start_response):
... start_response('200 OK', [('Content-Type', 'text/html')])
... return [ "<html>"
... "<body>"
... "<h1>Hello World!</h1>"
... "</body>"
... "</html>"
... ]
...
>>> def altered_wsgi_app(environ, start_response):
... response = Response.from_wsgi(wsgi_app1, environ, start_response)
... return response.add_headers(x_powered_by='fresco')(environ, start_response)
...
>>> print(TestApp(altered_wsgi_app).get('/').text())
200 OK
Content-Type: text/html
X-Powered-By: fresco
<html><body><h1>Hello World!</h1></body></html>
Common responses¶
Many canned responses are available as Response
class methods:
>>> from fresco.response import Response
>>> def view():
... if not somecondition():
... return Response.not_found()
... return Response(['ok'])
...
>>> def view2():
... if not somecondition():
... return Response.forbidden()
... return Response.redirect('http://www.example.com/')
...
API reference: fresco.response¶
The Response
class models the response from your application to a
single request.
- class fresco.response.Response(content=None, status=None, headers=None, onclose=None, _nocontent=[], passthrough=False, content_iterator=None, make_headers=<function make_headers>, **kwargs)[source]¶
Model an HTTP response
- add_cookie(name, value, max_age=None, expires=None, path='/', secure=None, domain=None, comment=None, httponly=False, samesite='Lax')[source]¶
Return a new response object with the given cookie added.
Synopsis:
>>> r = Response(content_type='text/plain') >>> r.headers [('Content-Type', 'text/plain')] >>> r.add_cookie('a', '1').headers [('Content-Type', 'text/plain'), ('Set-Cookie', 'a=1;Version=1')]
- add_header(name, value, make_header_name=<function make_header_name>)[source]¶
Return a new response object with the given additional header.
Synopsis:
>>> r = Response(content_type='text/plain') >>> r.headers [('Content-Type', 'text/plain')] >>> r.add_header('Cache-Control', 'no-cache').headers [('Content-Type', 'text/plain'), ('Cache-Control', 'no-cache')]
- add_headers(headers=[], **kwheaders)[source]¶
Return a new response object with the given additional headers.
Synopsis:
>>> r = Response(content_type='text/plain') >>> r.headers [('Content-Type', 'text/plain')] >>> r.add_headers( ... cache_control='no-cache', ... ).headers [('Content-Type', 'text/plain'), ('Cache-Control', 'no-cache')]
- add_onclose(*funcs)[source]¶
Add functions to be called as part of the response iterator’s
close
method.
- add_vary(*vary_on)[source]¶
Return a new response object with the given Vary header. Values specified will be added to any existing vary header.
Synopsis:
>>> r = Response().add_vary('Accept-Encoding') >>> r.headers [('Vary', 'Accept-Encoding')]
- classmethod bad_request(request=None)[source]¶
Return an HTTP bad request response.
Synopsis:
>>> def view(): ... return Response.bad_request() ...
- buffered()[source]¶
Return a new response object with the content buffered into a list. This will also generate a content-length header.
Example usage:
>>> def generate_content(): ... yield "one two " ... yield "three four five" ... >>> r = Response(content=generate_content()) >>> r.content <generator object ...> >>> r = Response(content=generate_content()).buffered() >>> r.content ['one two ', 'three four five']
- property content_type¶
Return the value of the
Content-Type
header if set, otherwiseNone
.
- classmethod error(message='500 Internal Server Error')[source]¶
Return an HTTP server error response (500).
Synopsis:
>>> def view(): ... return Response.error() ...
- classmethod forbidden(message='Sorry, access is denied')[source]¶
Return an HTTP forbidden response (403).
Synopsis:
>>> def view(): ... return Response.forbidden() ...
- classmethod from_wsgi(wsgi_callable, environ, start_response)[source]¶
Return a
Response
object constructed from the result of callingwsgi_callable
with the givenenviron
andstart_response
arguments.
- get_header(name, default='')[source]¶
Return the concatenated values of the named header(s) or
default
if the header has not been set.As specified in RFC2616 (section 4.2), multiple headers will be combined using a single comma.
Example usage:
>>> r = Response(set_cookie = ['cookie1', 'cookie2']) >>> r.get_header('set-cookie') 'cookie1,cookie2'
- get_headers(name)[source]¶
Return the list of headers set with the given name.
Synopsis:
>>> r = Response(set_cookie = ['cookie1', 'cookie2']) >>> r.get_headers('set-cookie') ['cookie1', 'cookie2']
- classmethod internal_server_error()[source]¶
Return an HTTP internal server error response (500).
Synopsis:
>>> def view(): ... return Response.internal_server_error() ...
- Returns:
A
fresco.response.Response
instance
- classmethod json(data, indent=None, separators=(', ', ':'), content_type='application/json', status=None, headers=None, dumps=<function dumps>, **kwargs)[source]¶
Return an
application/json
response with the given data JSON serialized- Parameters:
data – The data to json encode.
indent – The indent level. Defaults to
None
(no pretty printing)separators – Defaults to
(',', ':')
for the most compact JSON representationkwargs – Other keyword arguments are passed to
json.dumps
. These may be used to change encoding paramters, for example overriding the defaultJSONEncoder
class.
- classmethod length_required(request=None)[source]¶
Return an HTTP Length Required response (411).
Synopsis:
>>> def view(): ... return Response.length_required() ...
- classmethod meta_refresh(location, delay=1, request=None)[source]¶
Return an HTML page containing a <meta http-equiv=”refresh”> tag, causing the browser to redirect to the given location after
delay
seconds.- Parameters:
location – the URI of the new location. If relative this will be converted to an absolute URL based on the current request.
- classmethod method_not_allowed(valid_methods)[source]¶
Return an HTTP method not allowed response (405):
>>> from fresco import context >>> def view(): ... if context.request.method == 'POST': ... return Response.method_not_allowed(('POST', )) ...
- Parameters:
valid_methods – A list of HTTP methods valid for requested URL
- Returns:
A
fresco.response.Response
instance
- classmethod not_found(request=None)[source]¶
Return an HTTP not found response (404).
Synopsis:
>>> def view(): ... return Response.not_found() ...
- classmethod payload_too_large(request=None)[source]¶
Return an HTTP Payload Too Large response (413):
>>> response = Response.payload_too_large()
- classmethod redirect(url, fallback=None, status=302, _is_safe_url=<function is_safe_url>, allowed_hosts=frozenset({}), **kwargs)[source]¶
Return an HTTP redirect reponse (30x). Will only redirect to the current host or hosts in the
allowed_hosts
list. AValueError
will be raised if the URL is not permitted and no fallback is specified.- Parameters:
location – The redirect location or a view specification.
status – HTTP status code for the redirect, default is
STATUS_FOUND
(temporary redirect)fallback – a fallback URL to be used for the redirect in the case that
location
is considered unsafekwargs – kwargs to be passed to
fresco.core.urlfor()
to construct the redirect URL, or the fallback in the case thatlocation
is already a qualified URL.
Synopsis:
>>> def view(): ... return Response.redirect("/new-location") ...
The location argument is interpreted as for
unrestricted_redirect()
- classmethod redirect_permanent(*args, **kwargs)[source]¶
Return an HTTP permanent redirect reponse.
- Parameters:
location – the URI of the new location. If relative this will be converted to an absolute URL based on the current request.
- classmethod redirect_temporary(*args, **kwargs)[source]¶
Return an HTTP permanent redirect reponse.
- Parameters:
location – the URI of the new location. If relative this will be converted to an absolute URL based on the current request.
- remove_headers(*headers)[source]¶
Return a new response object with the named headers removed.
Synopsis:
>>> r = Response(content_type='text/plain', ... cache_control='no-cache') >>> r.headers [('Cache-Control', 'no-cache'), ('Content-Type', 'text/plain')] >>> r.remove_headers('Cache-Control').headers [('Content-Type', 'text/plain')]
- replace(content=None, status=None, headers=None, **kwheaders)[source]¶
Return a new response object with any of content, status or headers changed.
Synopsis:
>>> r = Response(content_type='text/html') >>> r = r.replace(content='foo', ... status=404, ... headers=[('Content-Type', 'text/plain')], ... content_length=3)
- classmethod request_entity_too_large(request=None)¶
Return an HTTP Payload Too Large response (413):
>>> response = Response.payload_too_large()
- property status_code¶
Return the numeric status code for the response as an integer:
>>> Response(status='404 Not Found').status_code 404 >>> Response(status=200).status_code 200
- classmethod unauthorized_basic(realm)[source]¶
Return an HTTP unauthorized response (401) with a WWW-Authenticate header set for HTTP Basic authentication..
- classmethod unrestricted_redirect(location, request=None, status=302, **kwargs)[source]¶
Return an HTTP redirect reponse (30x).
- Parameters:
location – The redirect location or a view specification.
status – HTTP status code for the redirect, default is
STATUS_FOUND
(temporary redirect)kwargs – kwargs to be passed to
fresco.core.urlfor()
to construct the redirect URL
Synopsis:
>>> def view(): ... return Response.redirect("/new-location") ...
The location parameter is interpreted as follows:
If it is a callable it is assumed to be a view, and passed to
fresco.core.urlfor()
, along with any keyword arguments, to generate the redirect URL.If it is a string and contains ‘://’ it is assumed to be an absolute URL and no further processing is done.
If it is a string and contains a slash, it is assumed to be a relative URL and resolved relative to the current request.
If it is any other string it is passed to
fresco.core.urlfor()
to resolve it to a URL. If this fails (ie raises RouteNotFound) it is assumed to be a relative URL and resolved relative to the current request.