-
-
Notifications
You must be signed in to change notification settings - Fork 16.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Registering a handler for HTTPException has no effect #941
Comments
Long story short: It is a bug, it's a flaw in the design, it's easy to work around as a user, so it's rather low priority to fix. :S |
Do you know where the bug exactly lies? If this is an already known bug, with some informations about it maybe I could help by trying to fix it by myself and then submit a pull-request. |
I think it would require rewriting most of the error handling system for Flask, the code for this is in |
Originally i intended to rewrite the error handling system to register an error handler for each subclass of HTTPException on initialization of the Flask object, removing the distinction between HTTP exceptions and other exception types. However, with the current behavior of preferring the first-registered handler when an error occurs, doing this without compromises would require changing this to preferring the last-registered handler, a backwards-incompatible change. The following is a very minimal fix to only make Flask prioritize the user's error handlers over others. I am not sure which performance implications this has. Maybe a major rewrite of the error handling system is still desirable (with more complex prioritization of errorhandlers -- something based on "distance" in inheritance tree?), but at the moment i don't see a reason to do so, given that the current system is primitive enough to be understandable, while still usable.
Originally i intended to rewrite the error handling system to register an error handler for each subclass of HTTPException on initialization of the Flask object, removing the distinction between HTTP exceptions and other exception types. However, with the current behavior of preferring the first-registered handler when an error occurs, doing this without compromises would require changing this to preferring the last-registered handler, a backwards-incompatible change. The following is a very minimal fix to only make Flask prioritize the user's error handlers over others. I am not sure which performance implications this has. Maybe a major rewrite of the error handling system is still desirable (with more complex prioritization of errorhandlers -- something based on "distance" in inheritance tree?), but at the moment i don't see a reason to do so, given that the current system is primitive enough to be understandable, while still usable.
#952 is a possible solution to this. |
Originally i intended to rewrite the error handling system to register an error handler for each subclass of HTTPException on initialization of the Flask object, removing the distinction between HTTP exceptions and other exception types. However, with the current behavior of preferring the first-registered handler when an error occurs, doing this without compromises would require changing this to preferring the last-registered handler, a backwards-incompatible change. The following is a very minimal fix to only make Flask prioritize the user's error handlers over others. I am not sure which performance implications this has. Maybe a major rewrite of the error handling system is still desirable (with more complex prioritization of errorhandlers -- something based on "distance" in inheritance tree?), but at the moment i don't see a reason to do so, given that the current system is primitive enough to be understandable, while still usable.
Fixed by #839 |
#839 doesn't fix the issue OP posted -- it only handles subclasses of |
Could you provide a testcase that fails? |
import unittest
from werkzeug.exceptions import HTTPException, NotFound
import flask
class ErrorHandlerTest(unittest.TestCase):
def setUp(self):
app = flask.Flask(__name__)
@app.errorhandler(HTTPException)
def http_exception(e):
return 'generic', 500
@app.errorhandler(NotFound)
def notfound_exception(e):
return 'not found', 404
@app.route('/error')
def error():
flask.abort(500)
self.app = app
def test_notfound_handler(self):
rv = self.app.test_client().get('/')
self.assertEqual(rv.status_code, 404)
self.assertEqual(rv.data, 'not found')
def test_http_handler(self):
rv = self.app.test_client().get('/error')
self.assertEqual(rv.status_code, 500)
self.assertEqual(rv.data, 'generic')
if __name__ == '__main__':
unittest.main() $ venv/bin/python app.py
F.
======================================================================
FAIL: test_http_handler (__main__.ErrorHandlerTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "app.py", line 35, in test_http_handler
self.assertEqual(rv.data, 'generic')
AssertionError: '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n<title>500 ...' != 'generic'
----------------------------------------------------------------------
Ran 2 tests in 0.021s
FAILED (failures=1) Code |
Ahh, I think this is because HTTPException doesn't have a HTTP code, but the error handling logic tries to find an errorhandler with the HTTP code. |
Has anybody reviewed/tested #1383? I was not very sure by looking at the changes. |
Yeah this slipped through the cracks! I wonder whether we shouldn't remove the code/no-code distinction because of this bug though. |
right, i didn’t think of this, sorry! so the legal classes to register handlers for: and the legal instances of exceptions to raise are instances of subclasses of this means the following assumptions hold: if isinstance(e_instance, HTTPException):
assert e_instance.code is not None
if issubclass(e_class, HTTPException):
assert e_class is HTTPException or e_class.code is not None we should add a way to add a handler for |
@Bekt that PR should be closed, it was written against the old broken behavior that I replaced. |
For those who simply want to override the default behavior of the default import flask
from werkzeug.exceptions import default_exceptions
def create_app():
app = flask.Flask(__name__)
....
for code, ex in default_exceptions:
app.errorhandler(code)(_handle_http_exception)
def _handle_http_exception(error):
if flask.request.is_json:
return jsonify({
'status_code': error.code,
'message': str(error),
'description': error.description
}), error.code
raise error.get_response() |
#2144 seems to solve this issue |
Error handlers are now returned in order of blueprint:code, app:code, blueprint:HTTPException, app:HTTPException, None Corresponding tests also added. Ref issue pallets#941, pr pallets#1383, pallets#2082, pallets#2144
Error handlers are now returned in order of blueprint:code, app:code, blueprint:HTTPException, app:HTTPException, None Corresponding tests also added. Ref issue pallets#941, pr pallets#1383, pallets#2082, pallets#2144
Error handlers are now returned in order of blueprint:code, app:code, blueprint:HTTPException, app:HTTPException, None Corresponding tests also added. Ref issue pallets#941, pr pallets#1383, pallets#2082, pallets#2144
Resolved by #2314 |
@Bekt I'm a bit late to the party, hope you're around... I'm trying to get flask to return JSON errors when there are exceptions. Your code works great except for
This results in |
@antgel i would be lying if i said i remember even remotely what this thread is about... i miss my flask days. |
I mean, the error message is pretty clear: |
Hi So late, but it is NOT a bug, this is a design decision. HTTPException is the base type for all http exceptions in werkzeug. |
When registering a handler for
werkzeug.exceptions.HTTPException
, it has no effect when an HTTP error is raised.Assume the following handler:
When requesting a page for which no route exists, a JSON response should be returned by the error handler, but instead, the usual
Flask
-generated HTTP error page is returned.On the other hand, if the error handler is defined to handle a specific error code (by passing the error code to the
app.errorhandler
decorator), the exception is trapped and the JSON message returned.As
wekzeug.exceptions.HTTPException
is the class raised internally by theabort()
function, why isn't it possible to create a "catch-all" handler like this? Am I missing something?The text was updated successfully, but these errors were encountered: