Index: django/utils/decorators.py
===================================================================
--- django/utils/decorators.py	(revision 817)
+++ django/utils/decorators.py	(working copy)
@@ -12,7 +12,14 @@
                 result = middleware.process_request(request)
                 if result is not None:
                     return result
-            response = view_func(request, *args, **kwargs)
+            try:
+                response = view_func(request, *args, **kwargs)
+            except Exception, e:
+                if hasattr(middleweare, 'process_exception'):
+                    result = middleware.process_exception(request, e)
+                    if result is not None:
+                        return result
+                raise e
             if hasattr(middleware, 'process_response'):
                 result = middleware.process_response(request, response)
                 if result is not None:
Index: django/core/handlers/base.py
===================================================================
--- django/core/handlers/base.py	(revision 817)
+++ django/core/handlers/base.py	(working copy)
@@ -2,7 +2,7 @@
 
 class BaseHandler:
     def __init__(self):
-        self._request_middleware = self._view_middleware = self._response_middleware = None
+        self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
 
     def load_middleware(self):
         """
@@ -15,6 +15,7 @@
         self._request_middleware = []
         self._view_middleware = []
         self._response_middleware = []
+        self._exception_middleware = []
         for middleware_path in settings.MIDDLEWARE_CLASSES:
             dot = middleware_path.rindex('.')
             mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
@@ -38,6 +39,8 @@
                 self._view_middleware.append(mw_instance.process_view)
             if hasattr(mw_instance, 'process_response'):
                 self._response_middleware.insert(0, mw_instance.process_response)
+            if hasattr(mw_instance, 'process_exception'):
+                self._exception_middleware.insert(0, mw_instance.process_exception)
 
     def get_response(self, path, request):
         "Returns an HttpResponse object for the given HttpRequest"
@@ -61,7 +64,16 @@
                 if response:
                     return response
 
-            response = callback(request, **param_dict)
+            try:
+                response = callback(request, **param_dict)
+            except Exception, e:
+                # if there is an exception, run it through exception middleware and
+                # if a response is given, return that. Otherwise reraise the exception.
+                for middleware_method in self._exception_middleware:
+                    response = middleware_method(request, e)
+                    if response:
+                        return response
+                raise e
 
             # Complain if the view returned None (a common error).
             if response is None:
Index: docs/middleware.txt
===================================================================
--- docs/middleware.txt	(revision 817)
+++ docs/middleware.txt	(working copy)
@@ -168,6 +168,18 @@
 the given ``response``, or it could create and return a brand-new
 ``HttpResponse``.
 
+process_exception
+-----------------
+
+Interface: ``process_exception(self, request, exception)``
+
+``request`` is an ``HttpRequest`` object. ``exception`` is an ``Exception``
+object raised by the view function.
+
+``process_exception()`` can return an ``HttpResponse`` object. If it does,
+that response will be returned to the browser. Otherwise system exception
+handling kicks in.
+
 Guidelines
 ----------
 
