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)
|
* Sriram Madapusi Vasudevan (TheSriram)
|
||||||
* Erik Erwitt (eerwitt)
|
* Erik Erwitt (eerwitt)
|
||||||
* Bernhard Weitzhofer (b6d)
|
* 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/request_and_response
|
||||||
api/status
|
api/status
|
||||||
api/errors
|
api/errors
|
||||||
|
api/middleware
|
||||||
api/hooks
|
api/hooks
|
||||||
api/routing
|
api/routing
|
||||||
api/util
|
api/util
|
||||||
|
|
||||||
|
|||||||
109
falcon/api.py
109
falcon/api.py
@@ -37,8 +37,8 @@ class API(object):
|
|||||||
Args:
|
Args:
|
||||||
media_type (str, optional): Default media type to use as the value for
|
media_type (str, optional): Default media type to use as the value for
|
||||||
the Content-Type header on responses. (default 'application/json')
|
the Content-Type header on responses. (default 'application/json')
|
||||||
middleware(object or list, optional): One or more objects (
|
middleware(object or list, optional): One or more objects
|
||||||
instantiated classes) that implement the following middleware
|
(instantiated classes) that implement the following middleware
|
||||||
component interface::
|
component interface::
|
||||||
|
|
||||||
class ExampleComponent(object):
|
class ExampleComponent(object):
|
||||||
@@ -52,66 +52,30 @@ class API(object):
|
|||||||
the on_* responder
|
the on_* responder
|
||||||
\"""
|
\"""
|
||||||
|
|
||||||
def process_response(self, req, resp)
|
def process_resource(self, req, resp, resource):
|
||||||
\"""Post-processing of the response (after routing).
|
\"""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
|
def process_response(self, req, resp, resource)
|
||||||
routes the request, or calls any hooks. For example, if a
|
\"""Post-processing of the response (after routing).
|
||||||
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.
|
|
||||||
|
|
||||||
|
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
|
request_type (Request, optional): Request-like class to use instead
|
||||||
of Falcon's default class. Among other things, this feature
|
of Falcon's default class. Among other things, this feature
|
||||||
affords inheriting from ``falcon.request.Request`` in order
|
affords inheriting from ``falcon.request.Request`` in order
|
||||||
@@ -203,15 +167,18 @@ class API(object):
|
|||||||
# e.g. a 404.
|
# e.g. a 404.
|
||||||
responder, params, resource = self._get_responder(req)
|
responder, params, resource = self._get_responder(req)
|
||||||
|
|
||||||
|
self._call_rsrc_mw(middleware_stack, req, resp, resource)
|
||||||
|
|
||||||
responder(req, resp, **params)
|
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:
|
except Exception as ex:
|
||||||
for err_type, err_handler in self._error_handlers:
|
for err_type, err_handler in self._error_handlers:
|
||||||
if isinstance(ex, err_type):
|
if isinstance(ex, err_type):
|
||||||
err_handler(ex, req, resp, params)
|
err_handler(ex, req, resp, params)
|
||||||
self._call_after_hooks(req, resp, resource)
|
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
|
# NOTE(kgriffs): The following line is not
|
||||||
# reported to be covered under Python 3.4 for
|
# reported to be covered under Python 3.4 for
|
||||||
@@ -232,13 +199,13 @@ class API(object):
|
|||||||
# process_response when no error_handler is given
|
# process_response when no error_handler is given
|
||||||
# and for whatever exception. If an HTTPError is raised
|
# and for whatever exception. If an HTTPError is raised
|
||||||
# remaining process_response will be executed later.
|
# 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
|
raise
|
||||||
|
|
||||||
except HTTPError as ex:
|
except HTTPError as ex:
|
||||||
self._compose_error_response(req, resp, ex)
|
self._compose_error_response(req, resp, ex)
|
||||||
self._call_after_hooks(req, resp, resource)
|
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
|
# Set status and headers
|
||||||
@@ -528,20 +495,28 @@ class API(object):
|
|||||||
"""Run process_request middleware methods."""
|
"""Run process_request middleware methods."""
|
||||||
|
|
||||||
for component in self._middleware:
|
for component in self._middleware:
|
||||||
process_request, _ = component
|
process_request, _, _ = component
|
||||||
if process_request is not None:
|
if process_request is not None:
|
||||||
process_request(req, resp)
|
process_request(req, resp)
|
||||||
|
|
||||||
# Put executed component on the stack
|
# Put executed component on the stack
|
||||||
stack.append(component) # keep track from outside
|
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."""
|
"""Run process_response middleware."""
|
||||||
|
|
||||||
while stack:
|
while stack:
|
||||||
_, process_response = stack.pop()
|
_, _, process_response = stack.pop()
|
||||||
if process_response is not None:
|
if process_response is not None:
|
||||||
process_response(req, resp)
|
process_response(req, resp, resource)
|
||||||
|
|
||||||
def _call_after_hooks(self, req, resp, resource):
|
def _call_after_hooks(self, req, resp, resource):
|
||||||
"""Executes each of the global "after" hooks, in turn."""
|
"""Executes each of the global "after" hooks, in turn."""
|
||||||
|
|||||||
@@ -50,14 +50,17 @@ def prepare_middleware(middleware=None):
|
|||||||
for component in middleware:
|
for component in middleware:
|
||||||
process_request = util.get_bound_method(component,
|
process_request = util.get_bound_method(component,
|
||||||
'process_request')
|
'process_request')
|
||||||
|
process_resource = util.get_bound_method(component,
|
||||||
|
'process_resource')
|
||||||
process_response = util.get_bound_method(component,
|
process_response = util.get_bound_method(component,
|
||||||
'process_response')
|
'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'
|
msg = '{0} does not implement the middleware interface'
|
||||||
raise TypeError(msg.format(component))
|
raise TypeError(msg.format(component))
|
||||||
|
|
||||||
prepared_middleware.append((process_request, process_response))
|
prepared_middleware.append((process_request, process_resource,
|
||||||
|
process_response))
|
||||||
|
|
||||||
return prepared_middleware
|
return prepared_middleware
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class RequestIDComponent(object):
|
|||||||
def process_request(self, req, resp):
|
def process_request(self, req, resp):
|
||||||
req.context['request_id'] = '<generate ID>'
|
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'])
|
resp.set_header('X-Request-ID', req.context['request_id'])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ class RequestTimeMiddleware(object):
|
|||||||
global context
|
global context
|
||||||
context['start_time'] = datetime.utcnow()
|
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
|
global context
|
||||||
context['end_time'] = datetime.utcnow()
|
context['end_time'] = datetime.utcnow()
|
||||||
|
|
||||||
@@ -30,7 +34,12 @@ class ExecutedFirstMiddleware(object):
|
|||||||
context['executed_methods'].append(
|
context['executed_methods'].append(
|
||||||
'{0}.{1}'.format(self.__class__.__name__, 'process_request'))
|
'{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
|
global context
|
||||||
context['executed_methods'].append(
|
context['executed_methods'].append(
|
||||||
'{0}.{1}'.format(self.__class__.__name__, 'process_response'))
|
'{0}.{1}'.format(self.__class__.__name__, 'process_response'))
|
||||||
@@ -81,7 +90,7 @@ class TestRequestTimeMiddleware(TestMiddleware):
|
|||||||
"""Test that error in response middleware is propagated up"""
|
"""Test that error in response middleware is propagated up"""
|
||||||
class RaiseErrorMiddleware(object):
|
class RaiseErrorMiddleware(object):
|
||||||
|
|
||||||
def process_response(self, req, resp):
|
def process_response(self, req, resp, resource):
|
||||||
raise Exception("Always fail")
|
raise Exception("Always fail")
|
||||||
|
|
||||||
self.api = falcon.API(middleware=[RaiseErrorMiddleware()])
|
self.api = falcon.API(middleware=[RaiseErrorMiddleware()])
|
||||||
@@ -101,7 +110,10 @@ class TestRequestTimeMiddleware(TestMiddleware):
|
|||||||
self.assertEqual([{'status': 'ok'}], body)
|
self.assertEqual([{'status': 'ok'}], body)
|
||||||
self.assertEqual(self.srmock.status, falcon.HTTP_200)
|
self.assertEqual(self.srmock.status, falcon.HTTP_200)
|
||||||
self.assertIn("start_time", context)
|
self.assertIn("start_time", context)
|
||||||
|
self.assertIn("mid_time", context)
|
||||||
self.assertIn("end_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'],
|
self.assertTrue(context['end_time'] > context['start_time'],
|
||||||
"process_response not executed after request")
|
"process_response not executed after request")
|
||||||
|
|
||||||
@@ -137,7 +149,10 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
self.assertIn("transaction_id", context)
|
self.assertIn("transaction_id", context)
|
||||||
self.assertEqual("unique-req-id", context['transaction_id'])
|
self.assertEqual("unique-req-id", context['transaction_id'])
|
||||||
self.assertIn("start_time", context)
|
self.assertIn("start_time", context)
|
||||||
|
self.assertIn("mid_time", context)
|
||||||
self.assertIn("end_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'],
|
self.assertTrue(context['end_time'] > context['start_time'],
|
||||||
"process_response not executed after request")
|
"process_response not executed after request")
|
||||||
|
|
||||||
@@ -156,6 +171,8 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
expectedExecutedMethods = [
|
expectedExecutedMethods = [
|
||||||
"ExecutedFirstMiddleware.process_request",
|
"ExecutedFirstMiddleware.process_request",
|
||||||
"ExecutedLastMiddleware.process_request",
|
"ExecutedLastMiddleware.process_request",
|
||||||
|
"ExecutedFirstMiddleware.process_resource",
|
||||||
|
"ExecutedLastMiddleware.process_resource",
|
||||||
"ExecutedLastMiddleware.process_response",
|
"ExecutedLastMiddleware.process_response",
|
||||||
"ExecutedFirstMiddleware.process_response"
|
"ExecutedFirstMiddleware.process_response"
|
||||||
]
|
]
|
||||||
@@ -181,6 +198,7 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
# RequestTimeMiddleware process_response should be executed
|
# RequestTimeMiddleware process_response should be executed
|
||||||
self.assertIn("transaction_id", context)
|
self.assertIn("transaction_id", context)
|
||||||
self.assertIn("start_time", context)
|
self.assertIn("start_time", context)
|
||||||
|
self.assertNotIn("mid_time", context)
|
||||||
self.assertIn("end_time", context)
|
self.assertIn("end_time", context)
|
||||||
|
|
||||||
def test_inner_mw_with_ex_handler_throw_exception(self):
|
def test_inner_mw_with_ex_handler_throw_exception(self):
|
||||||
@@ -189,7 +207,7 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
|
|
||||||
class RaiseErrorMiddleware(object):
|
class RaiseErrorMiddleware(object):
|
||||||
|
|
||||||
def process_request(self, req, resp):
|
def process_request(self, req, resp, resource):
|
||||||
raise Exception("Always fail")
|
raise Exception("Always fail")
|
||||||
|
|
||||||
self.api = falcon.API(middleware=[TransactionIdMiddleware(),
|
self.api = falcon.API(middleware=[TransactionIdMiddleware(),
|
||||||
@@ -208,6 +226,7 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
# RequestTimeMiddleware process_response should be executed
|
# RequestTimeMiddleware process_response should be executed
|
||||||
self.assertIn("transaction_id", context)
|
self.assertIn("transaction_id", context)
|
||||||
self.assertIn("start_time", context)
|
self.assertIn("start_time", context)
|
||||||
|
self.assertNotIn("mid_time", context)
|
||||||
self.assertIn("end_time", context)
|
self.assertIn("end_time", context)
|
||||||
self.assertIn("error_handler", context)
|
self.assertIn("error_handler", context)
|
||||||
|
|
||||||
@@ -236,6 +255,7 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
# Any mw is executed now...
|
# Any mw is executed now...
|
||||||
self.assertIn("transaction_id", context)
|
self.assertIn("transaction_id", context)
|
||||||
self.assertNotIn("start_time", context)
|
self.assertNotIn("start_time", context)
|
||||||
|
self.assertNotIn("mid_time", context)
|
||||||
self.assertNotIn("end_time", context)
|
self.assertNotIn("end_time", context)
|
||||||
self.assertIn("error_handler", context)
|
self.assertIn("error_handler", context)
|
||||||
|
|
||||||
@@ -245,7 +265,7 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
|
|
||||||
class RaiseErrorMiddleware(object):
|
class RaiseErrorMiddleware(object):
|
||||||
|
|
||||||
def process_response(self, req, resp):
|
def process_response(self, req, resp, resource):
|
||||||
raise Exception("Always fail")
|
raise Exception("Always fail")
|
||||||
|
|
||||||
self.api = falcon.API(middleware=[ExecutedFirstMiddleware(),
|
self.api = falcon.API(middleware=[ExecutedFirstMiddleware(),
|
||||||
@@ -253,7 +273,7 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
ExecutedLastMiddleware()])
|
ExecutedLastMiddleware()])
|
||||||
|
|
||||||
def handler(ex, req, resp, params):
|
def handler(ex, req, resp, params):
|
||||||
context['error_handler'] = True
|
pass
|
||||||
|
|
||||||
self.api.add_error_handler(Exception, handler)
|
self.api.add_error_handler(Exception, handler)
|
||||||
|
|
||||||
@@ -265,6 +285,8 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
expectedExecutedMethods = [
|
expectedExecutedMethods = [
|
||||||
"ExecutedFirstMiddleware.process_request",
|
"ExecutedFirstMiddleware.process_request",
|
||||||
"ExecutedLastMiddleware.process_request",
|
"ExecutedLastMiddleware.process_request",
|
||||||
|
"ExecutedFirstMiddleware.process_resource",
|
||||||
|
"ExecutedLastMiddleware.process_resource",
|
||||||
"ExecutedLastMiddleware.process_response",
|
"ExecutedLastMiddleware.process_response",
|
||||||
"ExecutedFirstMiddleware.process_response"
|
"ExecutedFirstMiddleware.process_response"
|
||||||
]
|
]
|
||||||
@@ -284,7 +306,7 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
ExecutedLastMiddleware()])
|
ExecutedLastMiddleware()])
|
||||||
|
|
||||||
def handler(ex, req, resp, params):
|
def handler(ex, req, resp, params):
|
||||||
context['error_handler'] = True
|
pass
|
||||||
|
|
||||||
self.api.add_error_handler(Exception, handler)
|
self.api.add_error_handler(Exception, handler)
|
||||||
|
|
||||||
@@ -299,6 +321,38 @@ class TestSeveralMiddlewares(TestMiddleware):
|
|||||||
]
|
]
|
||||||
self.assertEqual(expectedExecutedMethods, context['executed_methods'])
|
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):
|
class TestRemoveBasePathMiddleware(TestMiddleware):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user