Search code examples
pythonpython-3.xflaskflask-restfulflask-restplus

How to get Flask_restplus to use error handlers from app_errorhandler?


I am trying to use global error handlers(app_errorhandler) in my APIs but I am getting some problems. I have a blueprint for errors where I define global errors defined as:

from werkzeug.exceptions import NotFound
from flask_app.errors import error_bp

@error_bp.app_errorhandler(NotFound)
def not_found_error(error):
    return "Error 404", 404

and this works fine for all app routes, but not for APIs created by flask_restplus nor flask_restful(tried both). When I try to raise NotFound in them I get:

"The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. You have requested this URI [/user/info/1/] but did you mean /user/info// or /user/edit// or /user/login ?"

When I define a errorhandler in my API as:

@user_api.errorhandler(NotFound)
def new_error(error):
    return "Error handler in API 404"

It does not use this errorhandler in API but the global one(why? how?), so I got a way to use app_errorhandler but this is not a solution as I do not want to define for every API an errorhandler if I have global set up. All my APIs are created using the blueprint they are in.

So my question is: How do I use the app_errorhandler in APIs without defining errorhandler in every API?

Answers can be flask_restplus or flask_restful because I have no problem with switching from one to the other. Thanks in advance.

NOTE: There are a lot of workarounds but I think that apis should be able to use apps errors by default because blueprints can use them and apis inherit Blueprints

Project structure and code as per Mehdi Sadeghi request:

.
|____flask_test_app
| |____errors
|   |___ __init__.py
|   |___ handlers.py
| |____test_found
|   |___ __init__.py
|   |___ apis.py
| |_____ __init__.py

__init__.py:

from flask import Flask

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config_name)

    from .errors import error_bp
    app.register_blueprint(error_bp)

    from .test_found import test_found
    app.register_blueprint(test_found)

    return app

errors.__init__.py:

from flask import Blueprint
error_bp = Blueprint('error_bp', import_name='error_bp')

from flask_test_app.errors import handlers

errors.handlers.py:

from werkzeug.exceptions import NotFound
from flask_test_app.errors import error_bp

@error_bp.app_errorhandler(NotFound)
def not_found_error(error):
    return "Error 404", 404

test_found.__init__py:

from flask import Blueprint
from flask_restplus import Api

test_found = Blueprint('test_found', import_name='test_found',
                       url_prefix='/test_found')

test_found_api = Api(test_found)
from flask_test_app.test_found import apis

test_found.apis.py:

from flask_restplus import Resource
from flask_test_app.test_found import test_found, test_found_api
from werkzeug.exceptions import NotFound

@test_found.route('/test_not_found', methods=['GET'])
def test_not_found():
    raise NotFound
    return "Not found does not work"

class TestAPINotFound(Resource):
    def get(self):
        raise NotFound
        return "TEST NOT FOUND FOR APIs"

test_found_api.add_resource(TestAPINotFound,
                      '/test_api_not_found',
                      endpoint='user_test')

Solution

  • In flask-restful, as described in the docs you can pass an errors dictionary to the api and customize the returned message:

    errors = {
        'NotFound': {
            'message': "Something is missing.",
            'status': 404,
        }
    }
    
    api = Api(app, errors=errors)
    

    Moreover it provides flask_restful.abort that you can use anywhere in your API:

    class HelloWorld(Resource):
        def get(self):
            if not self.has_permission():
                return flask_restful.abort(403)
            return {'hello': 'world'}
    

    You can also catch your exceptions such as NotFound and use flask_restful.abort as above.

    However, if you really need to customize the error handling you can subclass the Api and implement your own error handing before calling flask_restful.abort:

    class CustomApi(Api):
        def handle_error(self, e):
            # Do something and then abort
            flask_restful.abort('some error code...', 'error message')
    
    api = CustomApi(app, errors=errors)