From 9cd32cac32433d7ea03cbd64de6ab5c8ea64a981 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 8 Sep 2016 11:55:59 +0300 Subject: [PATCH] Corrected after response for error handlers Before this change after request functions were not correctly invoked for error handlers. --- CHANGES | 2 ++ flask/app.py | 29 +++++++++++++++++++++++++---- tests/test_basic.py | 23 +++++++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 76907082..ac53fbd5 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,8 @@ Version 0.12 (pull request ``#1730``). - Revert a behavior change that made the dev server crash instead of returning a Internal Server Error (pull request ``#2006``). +- Correctly invoke response handlers for both regular request dispatching as + well as error handlers. Version 0.11.2 -------------- diff --git a/flask/app.py b/flask/app.py index deedc83a..40fedda6 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1555,7 +1555,7 @@ class Flask(_PackageBoundObject): self.log_exception((exc_type, exc_value, tb)) if handler is None: return InternalServerError() - return handler(e) + return self.finalize_request(handler(e), from_error_handler=True) def log_exception(self, exc_info): """Logs an exception. This is called by :meth:`handle_exception` @@ -1623,9 +1623,30 @@ class Flask(_PackageBoundObject): rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) + return self.finalize_request(rv) + + def finalize_request(self, rv, from_error_handler=False): + """Given the return value from a view function this finalizes + the request by converting it into a repsonse and invoking the + postprocessing functions. This is invoked for both normal + request dispatching as well as error handlers. + + Because this means that it might be called as a result of a + failure a special safe mode is available which can be enabled + with the `from_error_handler` flag. If enabled failures in + response processing will be logged and otherwise ignored. + + :internal: + """ response = self.make_response(rv) - response = self.process_response(response) - request_finished.send(self, response=response) + try: + response = self.process_response(response) + request_finished.send(self, response=response) + except Exception: + if not from_error_handler: + raise + self.logger.exception('Request finalizing failed with an ' + 'error while handling an error') return response def try_trigger_before_first_request_functions(self): @@ -1972,7 +1993,7 @@ class Flask(_PackageBoundObject): response = self.full_dispatch_request() except Exception as e: error = e - response = self.make_response(self.handle_exception(e)) + response = self.handle_exception(e) return response(environ, start_response) finally: if self.should_ignore_error(error): diff --git a/tests/test_basic.py b/tests/test_basic.py index 33f6ec07..be3d5edd 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -768,6 +768,29 @@ def test_error_handling(): assert b'forbidden' == rv.data +def test_error_handling_processing(): + app = flask.Flask(__name__) + app.config['LOGGER_HANDLER_POLICY'] = 'never' + + @app.errorhandler(500) + def internal_server_error(e): + return 'internal server error', 500 + + @app.route('/') + def broken_func(): + 1 // 0 + + @app.after_request + def after_request(resp): + resp.mimetype = 'text/x-special' + return resp + + with app.test_client() as c: + resp = c.get('/') + assert resp.mimetype == 'text/x-special' + assert resp.data == b'internal server error' + + def test_before_request_and_routing_errors(): app = flask.Flask(__name__)