feat(middleware): Add middleware method "process_resource"
Create a new middleware method process_resource that occurs after process_request and routing, and ensure that the resource object is accessible. Additionally, make the resource object available to process_response. Issue #400
This commit is contained in:
		
							
								
								
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							@@ -35,3 +35,4 @@ below in order of date of first contribution:
 | 
			
		||||
* Sriram Madapusi Vasudevan (TheSriram)
 | 
			
		||||
* Erik Erwitt (eerwitt)
 | 
			
		||||
* Bernhard Weitzhofer (b6d)
 | 
			
		||||
* Rahman Syed (rsyed83)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								doc/api/middleware.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								doc/api/middleware.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
.. _middleware:
 | 
			
		||||
 | 
			
		||||
Middleware
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
Middleware components execute both before and after the framework
 | 
			
		||||
routes the request.  Middleware is registered by passing components
 | 
			
		||||
to the :ref:`API class <api>` initializer.
 | 
			
		||||
 | 
			
		||||
The middleware interface is defined as follows:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    class ExampleComponent(object):
 | 
			
		||||
        def process_request(self, req, resp):
 | 
			
		||||
            """Process the request before routing it.
 | 
			
		||||
 | 
			
		||||
            Args:
 | 
			
		||||
                req: Request object that will eventually be
 | 
			
		||||
                    routed to an on_* responder method
 | 
			
		||||
                resp: Response object that will be routed to
 | 
			
		||||
                    the on_* responder
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        def process_resource(self, req, resp, resource):
 | 
			
		||||
            """Process the request after routing.
 | 
			
		||||
 | 
			
		||||
            Args:
 | 
			
		||||
                req: Request object that will be passed to the
 | 
			
		||||
                    routed responder
 | 
			
		||||
                resp: Response object that will be passed to the
 | 
			
		||||
                    responder
 | 
			
		||||
                resource: Resource object to which the request was
 | 
			
		||||
                    routed. May be None if no route was found for
 | 
			
		||||
                    the request
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        def process_response(self, req, resp, resource)
 | 
			
		||||
            """Post-processing of the response (after routing).
 | 
			
		||||
 | 
			
		||||
            Args:
 | 
			
		||||
                req: Request object
 | 
			
		||||
                resp: Response object
 | 
			
		||||
                resource: Resource object to which the request was
 | 
			
		||||
                    routed. May be None if no route was found
 | 
			
		||||
                    for the request
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
Because middleware can execute before routing has occurred, if a
 | 
			
		||||
component modifies ``req.uri`` in its *process_request* method,
 | 
			
		||||
the framework will use the modified value to route the request.
 | 
			
		||||
 | 
			
		||||
Each component's *process_request*, *process_resource*, and
 | 
			
		||||
*process_response* methods are executed hierarchically, as a stack.
 | 
			
		||||
For example, if a list of middleware objects are passed as
 | 
			
		||||
``[mob1, mob2, mob3]``, the order of execution is as follows::
 | 
			
		||||
 | 
			
		||||
    mob1.process_request
 | 
			
		||||
        mob2.process_request
 | 
			
		||||
            mob3.process_request
 | 
			
		||||
                mob1.process_resource
 | 
			
		||||
                    mob2.process_resource
 | 
			
		||||
                        mob3.process_resource
 | 
			
		||||
                <route to responder method>
 | 
			
		||||
            mob3.process_response
 | 
			
		||||
        mob2.process_response
 | 
			
		||||
    mob1.process_response
 | 
			
		||||
 | 
			
		||||
Note that each component need not implement all process_*
 | 
			
		||||
methods; in the case that one of the three methods is missing,
 | 
			
		||||
it is treated as a noop in the stack. For example, if ``mob2`` did
 | 
			
		||||
not implement *process_request* and ``mob3`` did not implement
 | 
			
		||||
*process_response*, the execution order would look
 | 
			
		||||
like this::
 | 
			
		||||
 | 
			
		||||
    mob1.process_request
 | 
			
		||||
        _
 | 
			
		||||
            mob3.process_request
 | 
			
		||||
                mob1.process_resource
 | 
			
		||||
                    mob2.process_resource
 | 
			
		||||
                        mob3.process_resource
 | 
			
		||||
                <route to responder method>
 | 
			
		||||
            _
 | 
			
		||||
        mob2.process_response
 | 
			
		||||
    mob1.process_response
 | 
			
		||||
 | 
			
		||||
If one of the *process_request* middleware methods raises an
 | 
			
		||||
error, it will be processed according to the error type. If
 | 
			
		||||
the type matches a registered error handler, that handler will
 | 
			
		||||
be invoked and then the framework will begin to unwind the
 | 
			
		||||
stack, skipping any lower layers. The error handler may itself
 | 
			
		||||
raise an instance of HTTPError, in which case the framework
 | 
			
		||||
will use the latter exception to update the *resp* object.
 | 
			
		||||
Regardless, the framework will continue unwinding the middleware
 | 
			
		||||
stack. For example, if *mob2.process_request* were to raise an
 | 
			
		||||
error, the framework would execute the stack as follows::
 | 
			
		||||
 | 
			
		||||
    mob1.process_request
 | 
			
		||||
        mob2.process_request
 | 
			
		||||
            <skip mob1/mob2 process_resource, mob3, and routing>
 | 
			
		||||
        mob2.process_response
 | 
			
		||||
    mob1.process_response
 | 
			
		||||
 | 
			
		||||
Finally, if one of the *process_response* methods raises an error,
 | 
			
		||||
or the routed on_* responder method itself raises an error, the
 | 
			
		||||
exception will be handled in a similar manner as above. Then,
 | 
			
		||||
the framework will execute any remaining middleware on the
 | 
			
		||||
stack.
 | 
			
		||||
@@ -115,7 +115,7 @@ Classes and Functions
 | 
			
		||||
   api/request_and_response
 | 
			
		||||
   api/status
 | 
			
		||||
   api/errors
 | 
			
		||||
   api/middleware
 | 
			
		||||
   api/hooks
 | 
			
		||||
   api/routing
 | 
			
		||||
   api/util
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								falcon/api.py
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								falcon/api.py
									
									
									
									
									
								
							@@ -37,8 +37,8 @@ class API(object):
 | 
			
		||||
    Args:
 | 
			
		||||
        media_type (str, optional): Default media type to use as the value for
 | 
			
		||||
            the Content-Type header on responses. (default 'application/json')
 | 
			
		||||
        middleware(object or list, optional): One or more objects (
 | 
			
		||||
            instantiated classes) that implement the following middleware
 | 
			
		||||
        middleware(object or list, optional): One or more objects
 | 
			
		||||
            (instantiated classes) that implement the following middleware
 | 
			
		||||
            component interface::
 | 
			
		||||
 | 
			
		||||
                class ExampleComponent(object):
 | 
			
		||||
@@ -52,66 +52,30 @@ class API(object):
 | 
			
		||||
                                the on_* responder
 | 
			
		||||
                        \"""
 | 
			
		||||
 | 
			
		||||
                    def process_response(self, req, resp)
 | 
			
		||||
                        \"""Post-processing of the response (after routing).
 | 
			
		||||
                    def process_resource(self, req, resp, resource):
 | 
			
		||||
                        \"""Process the request after routing.
 | 
			
		||||
 | 
			
		||||
                        Args:
 | 
			
		||||
                            req: Request object that will be passed to the
 | 
			
		||||
                                routed responder
 | 
			
		||||
                            resp: Response object that will be passed to the
 | 
			
		||||
                                responder
 | 
			
		||||
                            resource: Resource object to which the request was
 | 
			
		||||
                                routed. May be None if no route was found for
 | 
			
		||||
                                the request
 | 
			
		||||
                        \"""
 | 
			
		||||
 | 
			
		||||
            Middleware components execute both before and after the framework
 | 
			
		||||
            routes the request, or calls any hooks. For example, if a
 | 
			
		||||
            component modifies ``req.uri`` in its *process_request* method,
 | 
			
		||||
            the framework will use the modified value to route the request.
 | 
			
		||||
 | 
			
		||||
            Each component's *process_request* and *process_response* methods
 | 
			
		||||
            are executed hierarchically, as a stack. For example, if a list of
 | 
			
		||||
            middleware objects are passed as ``[mob1, mob2, mob3]``, the order
 | 
			
		||||
            of execution is as follows::
 | 
			
		||||
 | 
			
		||||
                mob1.process_request
 | 
			
		||||
                    mob2.process_request
 | 
			
		||||
                        mob3.process_request
 | 
			
		||||
                            <route to responder method>
 | 
			
		||||
                        mob3.process_response
 | 
			
		||||
                    mob2.process_response
 | 
			
		||||
                mob1.process_response
 | 
			
		||||
 | 
			
		||||
            Note that each component need not implement both process_*
 | 
			
		||||
            methods; in the case that one of the two methods is missing,
 | 
			
		||||
            it is treated as a noop in the stack. For example, if ``mob2`` did
 | 
			
		||||
            not implement *process_request* and ``mob3`` did not implement
 | 
			
		||||
            *process_response*, the execution order would look
 | 
			
		||||
            like this::
 | 
			
		||||
 | 
			
		||||
                mob1.process_request
 | 
			
		||||
                    _
 | 
			
		||||
                        mob3.process_request
 | 
			
		||||
                            <route to responder method>
 | 
			
		||||
                        _
 | 
			
		||||
                    mob2.process_response
 | 
			
		||||
                mob1.process_response
 | 
			
		||||
 | 
			
		||||
            If one of the *process_request* middleware methods raises an
 | 
			
		||||
            error, it will be processed according to the error type. If
 | 
			
		||||
            the type matches a registered error handler, that handler will
 | 
			
		||||
            be invoked and then the framework will begin to unwind the
 | 
			
		||||
            stack, skipping any lower layers. The error handler may itself
 | 
			
		||||
            raise an instance of HTTPError, in which case the framework
 | 
			
		||||
            will use the latter exception to update the *resp* object.
 | 
			
		||||
            Regardless, the framework will continue unwinding the middleware
 | 
			
		||||
            stack. For example, if *mob2.process_request* were to raise an
 | 
			
		||||
            error, the framework would execute the stack as follows::
 | 
			
		||||
 | 
			
		||||
                mob1.process_request
 | 
			
		||||
                    mob2.process_request
 | 
			
		||||
                        <skip mob3 and routing>
 | 
			
		||||
                    mob2.process_response
 | 
			
		||||
                mob1.process_response
 | 
			
		||||
 | 
			
		||||
            Finally, if one of the *process_response* methods raises an error,
 | 
			
		||||
            or the routed on_* responder method itself raises an error, the
 | 
			
		||||
            exception will be handled in a similar manner as above. Then,
 | 
			
		||||
            the framework will execute any remaining middleware on the
 | 
			
		||||
            stack.
 | 
			
		||||
                    def process_response(self, req, resp, resource)
 | 
			
		||||
                        \"""Post-processing of the response (after routing).
 | 
			
		||||
 | 
			
		||||
                        Args:
 | 
			
		||||
                            req: Request object
 | 
			
		||||
                            resp: Response object
 | 
			
		||||
                            resource: Resource object to which the request was
 | 
			
		||||
                                routed. May be None if no route was found
 | 
			
		||||
                                for the request
 | 
			
		||||
                        \"""
 | 
			
		||||
            See also :ref:`Middleware <middleware>`.
 | 
			
		||||
        request_type (Request, optional): Request-like class to use instead
 | 
			
		||||
            of Falcon's default class. Among other things, this feature
 | 
			
		||||
            affords inheriting from ``falcon.request.Request`` in order
 | 
			
		||||
@@ -203,15 +167,18 @@ class API(object):
 | 
			
		||||
                # e.g. a 404.
 | 
			
		||||
                responder, params, resource = self._get_responder(req)
 | 
			
		||||
 | 
			
		||||
                self._call_rsrc_mw(middleware_stack, req, resp, resource)
 | 
			
		||||
 | 
			
		||||
                responder(req, resp, **params)
 | 
			
		||||
                self._call_resp_mw(middleware_stack, req, resp)
 | 
			
		||||
                self._call_resp_mw(middleware_stack, req, resp, resource)
 | 
			
		||||
 | 
			
		||||
            except Exception as ex:
 | 
			
		||||
                for err_type, err_handler in self._error_handlers:
 | 
			
		||||
                    if isinstance(ex, err_type):
 | 
			
		||||
                        err_handler(ex, req, resp, params)
 | 
			
		||||
                        self._call_after_hooks(req, resp, resource)
 | 
			
		||||
                        self._call_resp_mw(middleware_stack, req, resp)
 | 
			
		||||
                        self._call_resp_mw(middleware_stack, req, resp,
 | 
			
		||||
                                           resource)
 | 
			
		||||
 | 
			
		||||
                        # NOTE(kgriffs): The following line is not
 | 
			
		||||
                        # reported to be covered under Python 3.4 for
 | 
			
		||||
@@ -232,13 +199,13 @@ class API(object):
 | 
			
		||||
                    # process_response when no error_handler is given
 | 
			
		||||
                    # and for whatever exception. If an HTTPError is raised
 | 
			
		||||
                    # remaining process_response will be executed later.
 | 
			
		||||
                    self._call_resp_mw(middleware_stack, req, resp)
 | 
			
		||||
                    self._call_resp_mw(middleware_stack, req, resp, resource)
 | 
			
		||||
                    raise
 | 
			
		||||
 | 
			
		||||
        except HTTPError as ex:
 | 
			
		||||
            self._compose_error_response(req, resp, ex)
 | 
			
		||||
            self._call_after_hooks(req, resp, resource)
 | 
			
		||||
            self._call_resp_mw(middleware_stack, req, resp)
 | 
			
		||||
            self._call_resp_mw(middleware_stack, req, resp, resource)
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
        # Set status and headers
 | 
			
		||||
@@ -528,20 +495,28 @@ class API(object):
 | 
			
		||||
        """Run process_request middleware methods."""
 | 
			
		||||
 | 
			
		||||
        for component in self._middleware:
 | 
			
		||||
            process_request, _ = component
 | 
			
		||||
            process_request, _, _ = component
 | 
			
		||||
            if process_request is not None:
 | 
			
		||||
                process_request(req, resp)
 | 
			
		||||
 | 
			
		||||
            # Put executed component on the stack
 | 
			
		||||
            stack.append(component)  # keep track from outside
 | 
			
		||||
 | 
			
		||||
    def _call_resp_mw(self, stack, req, resp):
 | 
			
		||||
    def _call_rsrc_mw(self, stack, req, resp, resource):
 | 
			
		||||
        """Run process_resource middleware methods."""
 | 
			
		||||
 | 
			
		||||
        for component in self._middleware:
 | 
			
		||||
            _, process_resource, _ = component
 | 
			
		||||
            if process_resource is not None:
 | 
			
		||||
                process_resource(req, resp, resource)
 | 
			
		||||
 | 
			
		||||
    def _call_resp_mw(self, stack, req, resp, resource):
 | 
			
		||||
        """Run process_response middleware."""
 | 
			
		||||
 | 
			
		||||
        while stack:
 | 
			
		||||
            _, process_response = stack.pop()
 | 
			
		||||
            _, _, process_response = stack.pop()
 | 
			
		||||
            if process_response is not None:
 | 
			
		||||
                process_response(req, resp)
 | 
			
		||||
                process_response(req, resp, resource)
 | 
			
		||||
 | 
			
		||||
    def _call_after_hooks(self, req, resp, resource):
 | 
			
		||||
        """Executes each of the global "after" hooks, in turn."""
 | 
			
		||||
 
 | 
			
		||||
@@ -50,14 +50,17 @@ def prepare_middleware(middleware=None):
 | 
			
		||||
    for component in middleware:
 | 
			
		||||
        process_request = util.get_bound_method(component,
 | 
			
		||||
                                                'process_request')
 | 
			
		||||
        process_resource = util.get_bound_method(component,
 | 
			
		||||
                                                 'process_resource')
 | 
			
		||||
        process_response = util.get_bound_method(component,
 | 
			
		||||
                                                 'process_response')
 | 
			
		||||
 | 
			
		||||
        if not (process_request or process_response):
 | 
			
		||||
        if not (process_request or process_resource or process_response):
 | 
			
		||||
            msg = '{0} does not implement the middleware interface'
 | 
			
		||||
            raise TypeError(msg.format(component))
 | 
			
		||||
 | 
			
		||||
        prepared_middleware.append((process_request, process_response))
 | 
			
		||||
        prepared_middleware.append((process_request, process_resource,
 | 
			
		||||
                                    process_response))
 | 
			
		||||
 | 
			
		||||
    return prepared_middleware
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ class RequestIDComponent(object):
 | 
			
		||||
    def process_request(self, req, resp):
 | 
			
		||||
        req.context['request_id'] = '<generate ID>'
 | 
			
		||||
 | 
			
		||||
    def process_response(self, req, resp):
 | 
			
		||||
    def process_response(self, req, resp, resource):
 | 
			
		||||
        resp.set_header('X-Request-ID', req.context['request_id'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,11 @@ class RequestTimeMiddleware(object):
 | 
			
		||||
        global context
 | 
			
		||||
        context['start_time'] = datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
    def process_response(self, req, resp):
 | 
			
		||||
    def process_resource(self, req, resp, resource):
 | 
			
		||||
        global context
 | 
			
		||||
        context['mid_time'] = datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
    def process_response(self, req, resp, resource):
 | 
			
		||||
        global context
 | 
			
		||||
        context['end_time'] = datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +34,12 @@ class ExecutedFirstMiddleware(object):
 | 
			
		||||
        context['executed_methods'].append(
 | 
			
		||||
            '{0}.{1}'.format(self.__class__.__name__, 'process_request'))
 | 
			
		||||
 | 
			
		||||
    def process_response(self, req, resp):
 | 
			
		||||
    def process_resource(self, req, resp, resource):
 | 
			
		||||
        global context
 | 
			
		||||
        context['executed_methods'].append(
 | 
			
		||||
            '{0}.{1}'.format(self.__class__.__name__, 'process_resource'))
 | 
			
		||||
 | 
			
		||||
    def process_response(self, req, resp, resource):
 | 
			
		||||
        global context
 | 
			
		||||
        context['executed_methods'].append(
 | 
			
		||||
            '{0}.{1}'.format(self.__class__.__name__, 'process_response'))
 | 
			
		||||
@@ -81,7 +90,7 @@ class TestRequestTimeMiddleware(TestMiddleware):
 | 
			
		||||
        """Test that error in response middleware is propagated up"""
 | 
			
		||||
        class RaiseErrorMiddleware(object):
 | 
			
		||||
 | 
			
		||||
            def process_response(self, req, resp):
 | 
			
		||||
            def process_response(self, req, resp, resource):
 | 
			
		||||
                raise Exception("Always fail")
 | 
			
		||||
 | 
			
		||||
        self.api = falcon.API(middleware=[RaiseErrorMiddleware()])
 | 
			
		||||
@@ -101,7 +110,10 @@ class TestRequestTimeMiddleware(TestMiddleware):
 | 
			
		||||
        self.assertEqual([{'status': 'ok'}], body)
 | 
			
		||||
        self.assertEqual(self.srmock.status, falcon.HTTP_200)
 | 
			
		||||
        self.assertIn("start_time", context)
 | 
			
		||||
        self.assertIn("mid_time", context)
 | 
			
		||||
        self.assertIn("end_time", context)
 | 
			
		||||
        self.assertTrue(context['mid_time'] > context['start_time'],
 | 
			
		||||
                        "process_resource not executed after request")
 | 
			
		||||
        self.assertTrue(context['end_time'] > context['start_time'],
 | 
			
		||||
                        "process_response not executed after request")
 | 
			
		||||
 | 
			
		||||
@@ -137,7 +149,10 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
        self.assertIn("transaction_id", context)
 | 
			
		||||
        self.assertEqual("unique-req-id", context['transaction_id'])
 | 
			
		||||
        self.assertIn("start_time", context)
 | 
			
		||||
        self.assertIn("mid_time", context)
 | 
			
		||||
        self.assertIn("end_time", context)
 | 
			
		||||
        self.assertTrue(context['mid_time'] > context['start_time'],
 | 
			
		||||
                        "process_resource not executed after request")
 | 
			
		||||
        self.assertTrue(context['end_time'] > context['start_time'],
 | 
			
		||||
                        "process_response not executed after request")
 | 
			
		||||
 | 
			
		||||
@@ -156,6 +171,8 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
        expectedExecutedMethods = [
 | 
			
		||||
            "ExecutedFirstMiddleware.process_request",
 | 
			
		||||
            "ExecutedLastMiddleware.process_request",
 | 
			
		||||
            "ExecutedFirstMiddleware.process_resource",
 | 
			
		||||
            "ExecutedLastMiddleware.process_resource",
 | 
			
		||||
            "ExecutedLastMiddleware.process_response",
 | 
			
		||||
            "ExecutedFirstMiddleware.process_response"
 | 
			
		||||
        ]
 | 
			
		||||
@@ -181,6 +198,7 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
        # RequestTimeMiddleware process_response should be executed
 | 
			
		||||
        self.assertIn("transaction_id", context)
 | 
			
		||||
        self.assertIn("start_time", context)
 | 
			
		||||
        self.assertNotIn("mid_time", context)
 | 
			
		||||
        self.assertIn("end_time", context)
 | 
			
		||||
 | 
			
		||||
    def test_inner_mw_with_ex_handler_throw_exception(self):
 | 
			
		||||
@@ -189,7 +207,7 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
 | 
			
		||||
        class RaiseErrorMiddleware(object):
 | 
			
		||||
 | 
			
		||||
            def process_request(self, req, resp):
 | 
			
		||||
            def process_request(self, req, resp, resource):
 | 
			
		||||
                raise Exception("Always fail")
 | 
			
		||||
 | 
			
		||||
        self.api = falcon.API(middleware=[TransactionIdMiddleware(),
 | 
			
		||||
@@ -208,6 +226,7 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
        # RequestTimeMiddleware process_response should be executed
 | 
			
		||||
        self.assertIn("transaction_id", context)
 | 
			
		||||
        self.assertIn("start_time", context)
 | 
			
		||||
        self.assertNotIn("mid_time", context)
 | 
			
		||||
        self.assertIn("end_time", context)
 | 
			
		||||
        self.assertIn("error_handler", context)
 | 
			
		||||
 | 
			
		||||
@@ -236,6 +255,7 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
        # Any mw is executed now...
 | 
			
		||||
        self.assertIn("transaction_id", context)
 | 
			
		||||
        self.assertNotIn("start_time", context)
 | 
			
		||||
        self.assertNotIn("mid_time", context)
 | 
			
		||||
        self.assertNotIn("end_time", context)
 | 
			
		||||
        self.assertIn("error_handler", context)
 | 
			
		||||
 | 
			
		||||
@@ -245,7 +265,7 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
 | 
			
		||||
        class RaiseErrorMiddleware(object):
 | 
			
		||||
 | 
			
		||||
            def process_response(self, req, resp):
 | 
			
		||||
            def process_response(self, req, resp, resource):
 | 
			
		||||
                raise Exception("Always fail")
 | 
			
		||||
 | 
			
		||||
        self.api = falcon.API(middleware=[ExecutedFirstMiddleware(),
 | 
			
		||||
@@ -253,7 +273,7 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
                                          ExecutedLastMiddleware()])
 | 
			
		||||
 | 
			
		||||
        def handler(ex, req, resp, params):
 | 
			
		||||
            context['error_handler'] = True
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.api.add_error_handler(Exception, handler)
 | 
			
		||||
 | 
			
		||||
@@ -265,6 +285,8 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
        expectedExecutedMethods = [
 | 
			
		||||
            "ExecutedFirstMiddleware.process_request",
 | 
			
		||||
            "ExecutedLastMiddleware.process_request",
 | 
			
		||||
            "ExecutedFirstMiddleware.process_resource",
 | 
			
		||||
            "ExecutedLastMiddleware.process_resource",
 | 
			
		||||
            "ExecutedLastMiddleware.process_response",
 | 
			
		||||
            "ExecutedFirstMiddleware.process_response"
 | 
			
		||||
        ]
 | 
			
		||||
@@ -284,7 +306,7 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
                                          ExecutedLastMiddleware()])
 | 
			
		||||
 | 
			
		||||
        def handler(ex, req, resp, params):
 | 
			
		||||
            context['error_handler'] = True
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.api.add_error_handler(Exception, handler)
 | 
			
		||||
 | 
			
		||||
@@ -299,6 +321,38 @@ class TestSeveralMiddlewares(TestMiddleware):
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(expectedExecutedMethods, context['executed_methods'])
 | 
			
		||||
 | 
			
		||||
    def test_order_mw_executed_when_exception_in_rsrc(self):
 | 
			
		||||
        """Test that error in inner middleware leaves"""
 | 
			
		||||
        global context
 | 
			
		||||
 | 
			
		||||
        class RaiseErrorMiddleware(object):
 | 
			
		||||
 | 
			
		||||
            def process_resource(self, req, resp, resource):
 | 
			
		||||
                raise Exception("Always fail")
 | 
			
		||||
 | 
			
		||||
        self.api = falcon.API(middleware=[ExecutedFirstMiddleware(),
 | 
			
		||||
                                          RaiseErrorMiddleware(),
 | 
			
		||||
                                          ExecutedLastMiddleware()])
 | 
			
		||||
 | 
			
		||||
        def handler(ex, req, resp, params):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.api.add_error_handler(Exception, handler)
 | 
			
		||||
 | 
			
		||||
        self.api.add_route(self.test_route, MiddlewareClassResource())
 | 
			
		||||
 | 
			
		||||
        self.simulate_request(self.test_route)
 | 
			
		||||
 | 
			
		||||
        # Any mw is executed now...
 | 
			
		||||
        expectedExecutedMethods = [
 | 
			
		||||
            "ExecutedFirstMiddleware.process_request",
 | 
			
		||||
            "ExecutedLastMiddleware.process_request",
 | 
			
		||||
            "ExecutedFirstMiddleware.process_resource",
 | 
			
		||||
            "ExecutedLastMiddleware.process_response",
 | 
			
		||||
            "ExecutedFirstMiddleware.process_response"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(expectedExecutedMethods, context['executed_methods'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestRemoveBasePathMiddleware(TestMiddleware):
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user