diff options
Diffstat (limited to 'repoze/bfg/view.py')
| -rw-r--r-- | repoze/bfg/view.py | 193 |
1 files changed, 82 insertions, 111 deletions
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py index 30076b775..1d1839530 100644 --- a/repoze/bfg/view.py +++ b/repoze/bfg/view.py @@ -1,4 +1,3 @@ -import cgi import mimetypes import os @@ -12,7 +11,6 @@ import os if hasattr(mimetypes, 'init'): mimetypes.init() -from webob import Response from webob.exc import HTTPFound import venusian @@ -20,7 +18,6 @@ import venusian from zope.deprecation import deprecated from zope.interface import providedBy -from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import IRoutesMapper from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import IViewClassifier @@ -447,129 +444,103 @@ class bfg_view(object): return wrapped -def default_view(context, request, status): +def default_exceptionresponse_view(context, request): if not isinstance(context, Exception): - # backwards compat for a default_view registered via + # backwards compat for an exception response view registered via # config.set_notfound_view or config.set_forbidden_view # instead of as a proper exception view - context = getattr(request, 'exception', None) - try: - msg = cgi.escape('%s' % context.args[0]) - except Exception: - msg = '' - html = """ - <html> - <title>%s</title> - <body> - <h1>%s</h1> - <code>%s</code> - </body> - </html> - """ % (status, status, msg) - headers = [('Content-Length', str(len(html))), - ('Content-Type', 'text/html')] - try: - registry = request.registry - except AttributeError: - registry = get_current_registry() - response_factory = registry.queryUtility(IResponseFactory, - default=Response) - return response_factory(status = status, - headerlist = headers, - app_iter = [html]) - -def default_forbidden_view(context, request): - return default_view(context, request, '401 Unauthorized') - -def default_notfound_view(context, request): - return default_view(context, request, '404 Not Found') - -def append_slash_notfound_view(context, request): - """For behavior like Django's ``APPEND_SLASH=True``, use this view - as the :term:`Not Found view` in your application. - - When this view is the Not Found view (indicating that no view was - found), and any routes have been defined in the configuration of - your application, if the value of the ``PATH_INFO`` WSGI - environment variable does not already end in a slash, and if the - value of ``PATH_INFO`` *plus* a slash matches any route's path, do - an HTTP redirect to the slash-appended PATH_INFO. Note that this - will *lose* ``POST`` data information (turning it into a GET), so - you shouldn't rely on this to redirect POST requests. - - If you use :term:`ZCML`, add the following to your application's - ``configure.zcml`` to use this view as the Not Found view:: + context = getattr(request, 'exception', context) + return context + +class AppendSlashNotFoundViewFactory(object): + """ There can only be one :term:`Not Found view` in any + :mod:`repoze.bfg application. Even if you use + :func:`repoze.bfg.view.append_slash_notfound_view` as the Not + Found view, :mod:`repoze.bfg` still must generate a ``404 Not + Found`` response when it cannot redirect to a slash-appended URL; + this not found response will be visible to site users. + + If you don't care what this 404 response looks like, and you only + need redirections to slash-appended route URLs, you may use the + :func:`repoze.bfg.view.append_slash_notfound_view` object as the + Not Found view. However, if you wish to use a *custom* notfound + view callable when a URL cannot be redirected to a slash-appended + URL, you may wish to use an instance of this class as the Not + Found view, supplying a :term:`view callable` to be used as the + custom notfound view as the first argument to its constructor. + For instance: - <view - context="repoze.bfg.exceptions.NotFound" - view="repoze.bfg.view.append_slash_notfound_view"/> + .. code-block:: python - Or use the - :meth:`repoze.bfg.configuration.Configurator.add_view` - method if you don't use ZCML:: + from repoze.bfg.exceptions import NotFound + from repoze.bfg.view import AppendSlashNotFoundViewFactory - from repoze.bfg.exceptions import NotFound - from repoze.bfg.view import append_slash_notfound_view - config.add_view(append_slash_notfound_view, context=NotFound) + def notfound_view(context, request): + return HTTPNotFound('It aint there, stop trying!') - See also :ref:`changing_the_notfound_view`. + custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view) + config.add_view(custom_append_slash, context=NotFound) - .. note:: This function is new as of :mod:`repoze.bfg` version 1.1. + The ``notfound_view`` supplied must adhere to the two-argument + view callable calling convention of ``(context, request)`` + (``context`` will be the exception object). - There can only be one Not Found view in any :mod:`repoze.bfg - application. If you use ``append_slash_notfound_view`` as the Not - Found view, it still must generate a NotFound response when it - cannot redirect to a slash-appended URL; this not found response - will be visible to site users. + .. note:: This class is new as of :mod:`repoze.bfg` version 1.3. - If you wish to use a custom notfound view callable when - ``append_slash_notfound_view`` does not redirect to a - slash-appended URL, use a wrapper function as the - :exc:`repoze.bfg.exceptions.NotFound` view; have this wrapper - attach a :term:`view callable` which returns a response to the - request object named ``custom_notfound_view`` before calling - ``append_slash_notfound_view``. For example: + """ + def __init__(self, notfound_view=None): + if notfound_view is None: + notfound_view = default_exceptionresponse_view + self.notfound_view = notfound_view + + def __call__(self, context, request): + if not isinstance(context, Exception): + # backwards compat for an append_notslash_view registered via + # config.set_notfound_view instead of as a proper exception view + context = getattr(request, 'exception', None) + path = request.environ.get('PATH_INFO', '/') + registry = request.registry + mapper = registry.queryUtility(IRoutesMapper) + if mapper is not None and not path.endswith('/'): + slashpath = path + '/' + for route in mapper.get_routes(): + if route.match(slashpath) is not None: + return HTTPFound(location=slashpath) + return self.notfound_view(context, request) + +append_slash_notfound_view = AppendSlashNotFoundViewFactory() +append_slash_notfound_view.__doc__ = """\ +For behavior like Django's ``APPEND_SLASH=True``, use this view as the +:term:`Not Found view` in your application. + +When this view is the Not Found view (indicating that no view was +found), and any routes have been defined in the configuration of your +application, if the value of the ``PATH_INFO`` WSGI environment +variable does not already end in a slash, and if the value of +``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP +redirect to the slash-appended PATH_INFO. Note that this will *lose* +``POST`` data information (turning it into a GET), so you shouldn't +rely on this to redirect POST requests. + +If you use :term:`ZCML`, add the following to your application's +``configure.zcml`` to use this view as the Not Found view:: - .. code-block:: python + <view + context="repoze.bfg.exceptions.NotFound" + view="repoze.bfg.view.append_slash_notfound_view"/> - from webob.exc import HTTPNotFound - from repoze.bfg.exceptions import NotFound - from repoze.bfg.view import append_slash_notfound_view +Or use the +:meth:`repoze.bfg.configuration.Configurator.add_view` +method if you don't use ZCML:: - def notfound_view(exc, request): - def fallback_notfound_view(exc, request): - return HTTPNotFound('It aint there, stop trying!') - request.fallback_notfound_view = fallback_notfound_view - return append_slash_notfound_view(exc, request) + from repoze.bfg.exceptions import NotFound + from repoze.bfg.view import append_slash_notfound_view + config.add_view(append_slash_notfound_view, context=NotFound) - config.add_view(notfound_view, context=NotFound) +See also :ref:`changing_the_notfound_view`. - ``custom_notfound_view`` must adhere to the two-argument view - callable calling convention of ``(context, request)`` (``context`` - will be the exception object). +.. note:: This function is new as of :mod:`repoze.bfg` version 1.1. +""" - If ``custom_notfound_view`` is not found on the request object, a - default notfound response will be generated when the - ``append_slash_notfound_view`` doesn't redirect to a - slash-appended URL. - .. note:: The checking for ``request.custom_notfound_view`` by - ``append_slash_notfound_view`` is new as of :mod:`repoze.bfg` - version 1.3. - """ - if not isinstance(context, Exception): - # backwards compat for an append_notslash_view registered via - # config.set_notfound_view instead of as a proper exception view - context = getattr(request, 'exception', None) - path = request.environ.get('PATH_INFO', '/') - registry = request.registry - mapper = registry.queryUtility(IRoutesMapper) - if mapper is not None and not path.endswith('/'): - slashpath = path + '/' - for route in mapper.get_routes(): - if route.match(slashpath) is not None: - return HTTPFound(location=slashpath) - notfound_view = getattr(request, 'custom_notfound_view', - default_notfound_view) - return notfound_view(context, request) |
