version:
Flask 0.11.1
Flask-RESTful 0.3.7
gunicorn 19.9.0
DEBUG config:
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_ECHO = False
Approximate code:
from flask import Blueprint
from flask_restful import Api
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
@api_bp.app_errorhandler(Exception)
def internal_error(e):
error = str(e)
if request.accept_mimetypes.accept_json:
response = jsonify(
{'error': 'exception error', 'message': error})
response.status_code = 500
logger = current_app.logger
logger.warning(traceback.format_exc())
db.session.rollback()
return response
return render_template('500.html'), 500
@api_bp.route('/hello')
def hello():
raise Exception
class world(Resource):
def get(self):
raise Exception
api.add_resource(world, '/world')
/hello and /world response is
{
"error": "exception error",
"message": ""
}
/hello response is
{
"error": "exception error",
"message": ""
}
/world response is
{
"message": "Internal Server Error"
}
@woshihaoren When call api = Api(api_bp), Api will replace the origin Flask App exception handler.
See flask_restful/__init__.py#L193
def _init_app(self, app):
"""Perform initialization actions with the given :class:`flask.Flask`
object.
:param app: The flask application object
:type app: flask.Flask
"""
app.handle_exception = partial(self.error_router, app.handle_exception)
app.handle_user_exception = partial(self.error_router, app.handle_user_exception)
if len(self.resources) > 0:
for resource, urls, kwargs in self.resources:
self._register_view(app, resource, *urls, **kwargs)
In error_router, check whether the exception is raised from a flask-restful endpoint handler.
def error_router(self, original_handler, e):
"""This function decides whether the error occured in a flask-restful
endpoint or not. If it happened in a flask-restful endpoint, our
handler will be dispatched. If it happened in an unrelated view, the
app's original error handler will be dispatched.
In the event that the error occurred in a flask-restful endpoint but
the local handler can't resolve the situation, the router will fall
back onto the original_handler as last resort.
:param original_handler: the original Flask error handler for the app
:type original_handler: function
:param e: the exception raised while handling the request
:type e: Exception
"""
if self._has_fr_route():
try:
return self.handle_error(e)
except Exception:
pass # Fall through to original handler
return original_handler(e)
handle_error check the exception type and the value of PROPAGATE_EXCEPTIONS flask_restful/__init__.py#L281
def handle_error(self, e):
"""Error handler for the API transforms a raised exception into a Flask
response, with the appropriate HTTP status code and body.
:param e: the raised Exception object
:type e: Exception
"""
got_request_exception.send(current_app._get_current_object(), exception=e)
if not isinstance(e, HTTPException) and current_app.propagate_exceptions:
exc_type, exc_value, tb = sys.exc_info()
if exc_value is e:
raise
else:
raise e
In DEBUG mode, PROPAGATE_EXCEPTIONS will be True. See also Flask doc.
Exceptions are re-raised rather than being handled by the app鈥檚 error handlers. If not set, this is implicitly true if TESTING or DEBUG is enabled.
So that is the reason of why it can work in DEBUG mode. There are some solution to solve the problem:
PROPAGATE_EXCEPTIONS = TrueApi class and override the handle_error method@Hanaasagi Perfect answer!
Most helpful comment
@woshihaoren When call
api = Api(api_bp),Apiwill replace the origin Flask App exception handler.See
flask_restful/__init__.py#L193In
error_router, check whether the exception is raised from a flask-restful endpoint handler.handle_errorcheck the exception type and the value ofPROPAGATE_EXCEPTIONSflask_restful/__init__.py#L281In DEBUG mode,
PROPAGATE_EXCEPTIONSwill beTrue. See also Flask doc.So that is the reason of why it can work in DEBUG mode. There are some solution to solve the problem:
PROPAGATE_EXCEPTIONS = TrueApiclass and override thehandle_errormethod