Provide tools.response.render_http_exception and use that
authorSebastian Spaeth <Sebastian@SSpaeth.de>
Sun, 23 Dec 2012 10:57:45 +0000 (11:57 +0100)
committerSebastian Spaeth <Sebastian@SSpaeth.de>
Sun, 23 Dec 2012 10:57:45 +0000 (11:57 +0100)
After the webob->werkzeug transition, controller functions can raise
werkzeug.HttpExceptions. We need to catch these in app.py when calling
the controller and handle them, rendering the corresponding error Response()
object. For consistency, we also want to allow meddleware functions to
raise HttpExceptions (e.g. the csrf meddleware needs to complain about lack
of cookies), so wrap the request and response parts of the meddleware too.

Finally, the urlmap.match() can also raise HttpExceptions, so we give it the
same treatment (render_http_exception). I am not sure, if we do not need to
handle the Redirect exception there in any different way though...

The new function render_http_exception makes use of the render_error infrastructure
to return a nicely templated error page. It also checks if the stock error
messages was used in cases where we have localizations (403, 404) and use those.

It is now possible to do things like "raise Forbidden(_('You suckr'))" or
raise NotFound(_('where is my left show again')) if you want to return
customized error messages to the user.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
mediagoblin/app.py
mediagoblin/tools/response.py

index 1a398bcdac4dc209b0a1862d6ee40539a365f025..d1f4cab7cb5ede93f0a2aee4afe2664281ccf243 100644 (file)
@@ -24,7 +24,7 @@ from werkzeug.exceptions import HTTPException, NotFound
 
 from mediagoblin import meddleware, __version__
 from mediagoblin.tools import common, translate, template
-from mediagoblin.tools.response import render_404
+from mediagoblin.tools.response import render_http_exception
 from mediagoblin.tools.theme import register_themes
 from mediagoblin.tools import request as mg_request
 from mediagoblin.mg_globals import setup_globals
@@ -188,12 +188,11 @@ class MediaGoblinApp(object):
         try:
             endpoint, url_values = map_adapter.match()
             request.matchdict = url_values
-        except NotFound as exc:
-            return render_404(request)(environ, start_response)
         except HTTPException as exc:
-            # exceptions that match() is documented to return:
-            # MethodNotAllowed, RequestRedirect TODO: need to handle ???
-            return exc(environ, start_response)
+            # Stop and render exception
+            return render_http_exception(
+                request, exc,
+                exc.get_description(environ))(environ, start_response)
 
         view_func = view_functions[endpoint]
 
@@ -209,19 +208,32 @@ class MediaGoblinApp(object):
             controller = view_func
 
         # pass the request through our meddleware classes
-        for m in self.meddleware:
-            response = m.process_request(request, controller)
-            if response is not None:
-                return response(environ, start_response)
+        try:
+            for m in self.meddleware:
+                response = m.process_request(request, controller)
+                if response is not None:
+                    return response(environ, start_response)
+        except HTTPException as e:
+            return render_http_exception(
+                request, e,
+                e.get_description(environ))(environ, start_response)
 
         request.start_response = start_response
 
-        # get the response from the controller
-        response = controller(request)
+        # get the Http response from the controller
+        try:
+            response = controller(request)
+        except HTTPException as e:
+            response = render_http_exception(
+                request, e, e.get_description(environ))
 
-        # pass the response through the meddleware
-        for m in self.meddleware[::-1]:
-            m.process_response(request, response)
+        # pass the response through the meddlewares
+        try:
+            for m in self.meddleware[::-1]:
+                m.process_response(request, response)
+        except HTTPException as e:
+            response = render_http_exeption(
+                request, e, e.get_description(environ))
 
         return response(environ, start_response)
 
index b02dd6b55208d7485ae6c2a2f422b4bccea7ceef..80df1f5a9951efcb35681ebfa96440b421c2b84e 100644 (file)
@@ -63,6 +63,25 @@ def render_404(request):
     return render_error(request, 404, err_msg=err_msg)
 
 
+def render_http_exception(request, exc, description):
+    """Return Response() given a werkzeug.HTTPException
+
+    :param exc: werkzeug.HTTPException or subclass thereof
+    :description: message describing the error."""
+    # If we were passed the HTTPException stock description on
+    # exceptions where we have localized ones, use those:
+    stock_desc = (description == exc.__class__.description)
+
+    if stock_desc and exc.code == 403:
+        return render_403(request)
+    elif stock_desc and exc.code == 404:
+        return render_404(request)
+
+    return render_error(request, title=exc.args[0],
+                        err_msg=description,
+                        status=exc.code)
+
+
 def redirect(request, *args, **kwargs):
     """Redirects to an URL, using urlgen params or location string