0001"""Utility functions available for use by Controllers
0002
0003``etag_cache``, ``redirect_to``, and ``abort``.
0004"""
0005import logging
0006import warnings
0007
0008import paste.httpexceptions as httpexceptions
0009
0010from routes import url_for
0011
0012import pylons
0013import pylons.legacy
0014
0015__all__ = ['abort', 'etag_cache', 'redirect_to']
0016
0017log = logging.getLogger(__name__)
0018
0019def etag_cache(key=None):
0020 """Use the HTTP Entity Tag cache for Browser side caching
0021
0022 If a "If-None-Match" header is found, and equivilant to ``key``, then
0023 a ``304`` HTTP message will be returned with the ETag to tell the browser
0024 that it should use its current cache of the page.
0025
0026 Otherwise, the ETag header will be added to the response headers.
0027
0028 Suggested use is within a Controller Action like so:
0029
0030 .. code-block:: Python
0031
0032 import pylons
0033
0034 class YourController(BaseController):
0035 def index(self):
0036 etag_cache(key=1)
0037 return render('/splash.mako')
0038
0039 .. Note::
0040 This works because etag_cache will raise an HTTPNotModified
0041 exception if the ETag recieved matches the key provided.
0042 """
0043 if_none_match = pylons.request.environ.get('HTTP_IF_NONE_MATCH', None)
0044 pylons.response.headers['ETag'] = key
0045 if str(key) == if_none_match:
0046 log.debug("ETag match, returning 304 HTTP Not Modified Response")
0047 raise httpexceptions.HTTPNotModified()
0048 else:
0049 log.debug("ETag didn't match, returning response object")
0050 return pylons.response
0051
0052
0053def abort(status_code=None, detail="", headers=None, comment=None):
0054 """Aborts the request immediately by returning an HTTP exception
0055
0056 In the event that the status_code is a 300 series error, the detail
0057 attribute will be used as the Location header should one not be specified
0058 in the headers attribute.
0059 """
0060 exc = httpexceptions.get_exception(status_code)(detail, headers, comment)
0061 log.debug("Aborting request, status: %s, detail: %r, headers: %r, "
0062 "comment: %r", status_code, detail, headers, comment)
0063 raise exc
0064
0065
0066def redirect_to(*args, **kargs):
0067 """Raises a redirect exception
0068
0069 Optionally, a _code variable may be passed with the status code of the
0070 redirect, ie:
0071
0072 .. code-block:: Python
0073
0074 redirect_to('home_page', _code=303)
0075
0076 ``Deprecated``
0077 A Response object can be passed in as _response which will have the headers
0078 and cookies extracted from it and added into the redirect issued."""
0079 response = kargs.pop('_response', None)
0080 status_code = kargs.pop('_code', 302)
0081 exc = httpexceptions.get_exception(status_code)
0082 found = exc(url_for(*args, **kargs))
0083 log.debug("Generating %s redirect" % status_code)
0084 if response:
0085 warnings.warn(pylons.legacy.redirect_response_warning,
0086 PendingDeprecationWarning, 2)
0087 log.debug("Merging provided Response object into redirect")
0088 if str(response.status_code).startswith('3'):
0089 found.code = response.status_code
0090 for c in response.cookies.values():
0091 found.headers.add('Set-Cookie', c.output(header=''))
0092 raise found