diff --git a/falcon/api.py b/falcon/api.py index 89e805b..65c3f8d 100644 --- a/falcon/api.py +++ b/falcon/api.py @@ -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. """ diff --git a/tests/test_error_handlers.py b/tests/test_error_handlers.py index 595d22c..66414e3 100644 --- a/tests/test_error_handlers.py +++ b/tests/test_error_handlers.py @@ -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')