I am trying to create a custom error handler in Flask 1.0.2 and Flask-RESTful 0.3.7, using the guidelines on the "Implementing API Exceptions" page. (Flask-RESTful has its own way of creating custom error messages, but since it doesn't seem to have a way to accept a customized error message at the time of the exception, I am trying to use the vanilla Flask method instead.)
from flask import Flask, jsonify
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
#########################################
class MyGenericException(Exception):
status_code = 500
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
@app.errorhandler(MyGenericException)
def handle_generic_error(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
#########################################
class TestMe(Resource):
def get(self):
raise MyGenericException('A generic error', status_code=501)
api.add_resource(TestMe, '/testme', endpoint='TestMe')
#########################################
if __name__ == '__main__':
app.run(debug=False)
Calling http://127.0.0.1:5000/testme only returns a generic "500 Internal Server Error" message, however, not a 501 error with my custom error text. It appears that MyGenericException
is being raised properly, but Flask seems to ignore it.
[2019-05-08 17:09:18,409] ERROR in app: Exception on /testme [GET]
Traceback (most recent call last):
File "C:\Users\testuser\Envs\testenv\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\testuser\Envs\testenv\lib\site-packages\flask\app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\testuser\Envs\testenv\lib\site-packages\flask_restful\__init__.py", line 458, in wrapper
resp = resource(*args, **kwargs)
File "C:\Users\testuser\Envs\testenv\lib\site-packages\flask\views.py", line 88, in view
return self.dispatch_request(*args, **kwargs)
File "C:\Users\testuser\Envs\testenv\lib\site-packages\flask_restful\__init__.py", line 573, in dispatch_request
resp = meth(*args, **kwargs)
File "C:/Users/testuser/Documents/PyCharm Projects/TestApp/testapp.py", line 32, in get
raise MyGenericException('A generic error', status_code=505)
MyGenericException
The @app.errorhandler
decorator seems to be properly set for the custom MyGenericException
Exception. Why is it not being handled by Flask?
Thanks to anyone who can help.
Referring to both this question, and the docs, the key piece of information I took from both is if your route is a Flask-RESTful one, which yours is, it will be handled by handle_error()
, and the only way to prevent or customise this is to implement your own API class, and override handle_error()
.
Following up on @dylanj.nz's answer, this vanilla-Flask error handling method, and this example of overriding Flask-RESTful's API , here's the method I settled on. It allows Flask-RESTful to handle HTTPException
types of exceptions, but passes everything else through to the default (Flask) handler, where custom error messages can be specified (an entire JSON object of key/value entries, if you want) at the time of the exception.
from flask_restful import Resource, Api as _Api, HTTPException
app = Flask(__name__)
# This new Exception will accept a message, a status code, and a
# payload of other values to be displayed as a JSON object
class FlaskGenericException(Exception):
status_code = 500 # default unless overridden
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
@app.errorhandler(FlaskGenericException)
def handle_flask_generic_error(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
# This overridden Flask-RESTful API class will keep Flask-RESTful
# from handling errors other than HTTPException ones.
class Api(_Api):
def error_router(self, original_handler, e):
# Override original error_router to only handle HTTPExceptions.
if self._has_fr_route() and isinstance(e, HTTPException):
try:
# Use Flask-RESTful's error handling method
return self.handle_error(e)
except Exception:
# Fall through to original handler (i.e. Flask)
pass
return original_handler(e)
api = Api(app)
class TestMe(Resource):
def get(self):
try:
ldapc = ldap.connection
except:
# message = first parameter. Other parameters come in as "payload"
raise FlaskGenericException('A generic error', status_code=505, payload={'user': 'John Doe', 'company': 'Foobar Corp.'})
api.add_resource(TestMe, '/testme', endpoint='TestMe')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With