doc(API): Document the order in which error handlers are visited (#846)

Document and add tests to confirm the LIFO order that error handlers
are visited when handling an exception.
This commit is contained in:
Kurt Griffiths
2016-08-15 14:14:22 -06:00
committed by Fran Fitzpatrick
parent 5040782f50
commit fd5a0ba587
2 changed files with 33 additions and 5 deletions

View File

@@ -375,6 +375,18 @@ class API(object):
def add_error_handler(self, exception, handler=None):
"""Registers a handler for a given exception error type.
A handler can either raise an instance of ``HTTPError``
or modify `resp` manually in order to communicate
information about the issue to the client.
Error handlers are matched in LIFO order. In other words, when
searching for an error handler to match a raised exception, and
more than one handler matches the exception type, the framework
will choose the one that was most recently registered.
Therefore, more general error handlers (e.g., for the
``Exception`` type) should be added first, to avoid masking more
specific handlers for subclassed types.
Args:
exception (type): Whenever an error occurs when handling a request
that is an instance of this exception class, the associated
@@ -396,10 +408,6 @@ class API(object):
# Convert to an instance of falcon.HTTPError
raise falcon.HTTPError(falcon.HTTP_792)
Note:
A handler can either raise an instance of ``HTTPError``
or modify `resp` manually in order to communicate
information about the issue to the client.
"""

View File

@@ -81,9 +81,29 @@ class TestErrorHandler(testing.TestCase):
self.assertEqual(result.status_code, 723)
self.assertEqual(result.text, 'error: CustomException')
def test_error_order(self):
def test_error_order_duplicate(self):
self.api.add_error_handler(Exception, capture_error)
self.api.add_error_handler(Exception, handle_error_first)
result = self.simulate_get()
self.assertEqual(result.text, 'first error handler')
def test_error_order_subclass(self):
self.api.add_error_handler(Exception, capture_error)
self.api.add_error_handler(CustomException, handle_error_first)
result = self.simulate_delete()
self.assertEqual(result.status_code, 200)
self.assertEqual(result.text, 'first error handler')
result = self.simulate_get()
self.assertEqual(result.status_code, 723)
self.assertEqual(result.text, 'error: Plain Exception')
def test_error_order_subclass_masked(self):
self.api.add_error_handler(CustomException, handle_error_first)
self.api.add_error_handler(Exception, capture_error)
result = self.simulate_delete()
self.assertEqual(result.status_code, 723)
self.assertEqual(result.text, 'error: CustomException')