Skip to content
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

Integration with flask-restful & error handling #141

Closed
ecatkins opened this issue Apr 20, 2018 · 14 comments
Closed

Integration with flask-restful & error handling #141

ecatkins opened this issue Apr 20, 2018 · 14 comments

Comments

@ecatkins
Copy link

Similar to #86, errors like SignatureExpiry are returning 500 rather than 4xx errors.

However I am using flask-restful which does not have an error_handler, so the solutions in the referenced issue do not apply.

Flask-restful is briefly mentioned on the releases page here , but setting application.config['PROPAGATE_EXCEPTIONS'] = True didn't have any effect for me.

Any insight into how I can correctly deal with error handling using flask-restful?

@vimalloc
Copy link
Owner

Minus the propagate_exceptions thing, I'm not sure of anything else that could cause problems with flask-restful right off hand. Could you provide a minimum example that I could use to troubleshoot this further?

@ecatkins
Copy link
Author

ecatkins commented Apr 23, 2018

Thanks for the reply! A stripped back version of my code looks like...

application = Flask(__name__)
application.config['PROPAGATE_EXCEPTIONS'] = True

api = Api(application)
jwt = JWTManager(application)

class SomeRoute(Resource):
     @jwt_required
    def post(self):
        return {'message':'success'}, 200

api.add_resource(SomeRoute, '/some_route', endpoint = 'some_route')

When I make a call to this route with an expired token, the following exception is raised jwt.exceptions.ExpiredSignatureError: Signature has expired and flask errors with 500. I assume the desired behavior is for this error message to be returned?

@vimalloc
Copy link
Owner

Thanks. Sorry, I've been a bit busy these past couple days, but I'll try to make time to look at this today. Cheers.

@vimalloc
Copy link
Owner

I have been unable to duplicate this so far. I am testing with the flask 1.0 release that just came out, so maybe that changed something?

Here is the code I use to test the issue:

from flask import Flask
from flask_jwt_extended import JWTManager, jwt_required
from flask_restful import Api, Resource

app = Flask(__name__)
app.config['PROPAGATE_EXCEPTIONS'] = True

api = Api(app)
jwt = JWTManager(app)

class SomeRoute(Resource):
    @jwt_required
    def post(self):
        return {'message':'success'}, 200

api.add_resource(SomeRoute, '/some_route', endpoint = 'some_route')

if __name__ == '__main__':
    app.run(debug=False)

And here is the results of making an api call:

$ http POST :5000/some_route
HTTP/1.0 401 UNAUTHORIZED
Content-Length: 39
Content-Type: application/json
Date: Fri, 27 Apr 2018 22:19:26 GMT
Server: Werkzeug/0.14.1 Python/3.6.4

{
    "msg": "Missing Authorization Header"
}

Could you take a look and see if you are still able to reproduce this, and if so what the full code and request looks like?

Thanks!

@cesarmarroquin
Copy link

I also encountered this issue. I could not replicate it at all in development, but was getting 500s instead of 401s in production and staging.

I was able to reproduce it by setting the debug setting to false in development mode, and the 500s showed up.

I was able to fix it by setting application.config['PROPAGATE_EXCEPTIONS'] = True

@vimalloc
Copy link
Owner

It sounds like everything is all cleared up here. If that's not the case, please feel free to comment and re-open this issue 👍

@alexander-ding
Copy link

I am encountering the issue, and

I also encountered this issue. I could not replicate it at all in development, but was getting 500s instead of 401s in production and staging.

I was able to reproduce it by setting the debug setting to false in development mode, and the 500s showed up.

I was able to fix it by setting application.config['PROPAGATE_EXCEPTIONS'] = True

doesn't exactly do the trick for my setup.

I'm using Flask-Restful's custom errors. It is true that setting application.config['PROPAGATE_EXCEPTIONS'] = True fixes the errors propagating from JWT, but this setting also causes any custom error message to show up as 500s, instead of 400s. So if PROPAGATE_EXCEPTIONS, JWT errors don't work. If not PROPAGATE_EXCEPTIONS, custom errors break. It's a bit of an unexpected dilemma.

Is there any way to work around this without requiring PROPAGATE_EXCEPTIONS to be set?

@paurakhsharma
Copy link

@alexding123 I am experiencing the same issue.
Did you find any workaround?

@alexander-ding
Copy link

@paurakhsharma

I think I found a workaround to integrate custom errors.
Here's my current setup:
Set PROPAGATE_EXCEPTIONS = False
Then overwrite the error handler flask_restful.Api object as such:

from flask import Blueprint
from flask_restful import Api as _Api

# some custom errors
class RoomDoesNotExist(Exception):
    pass

CUSTOM_ERRORS = {
'RoomDoesNotExist': {
        'message': "A room by that name does not exist.",
        'status': 404,
    },
}

class Api(_Api):
    def error_router(self, original_handler, e):
        """ Override original error_router to only custom errors and parsing error (from webargs)"""
        error_type = type(e).__name__.split(".")[-1] # extract the error class name as a string
        # if error can be handled by flask_restful's Api object, do so
        # otherwise, let Flask handle the error
        # the 'UnprocessableEntity' is included only because I'm also using webargs
        # feel free to omit it
        if self._has_fr_route() and error_type in list(CUSTOM_ERRORS) + ['UnprocessableEntity']:
            try:
                return self.handle_error(e)
            except Exception:
                pass  # Fall through to original handler

        return original_handler(e)

api_bp = Blueprint('api', __name__)
api = Api(api_bp, errors=CUSTOM_ERRORS)

This way, flask_restful's errors are handled by flask_restful's Api object, and other errors fall through to Flask's error handler.

@tumluliu
Copy link

tumluliu commented Apr 2, 2020

@alexding123 many many thanks for your solution! That's the only way I've found so far to bypass this annoying issue

@mxhr
Copy link

mxhr commented Oct 7, 2021

Another solution for anyone who is hitting the 500 internal server error instead of the expected 401, 4XX error as expected when using flask_restful:

from flask import Flask
from flask_restful import Resource, Api
from flask_jwt import JWT, jwt_required

app = Flask(__name__)
app.secret_key = "yourSecretKeyHere"
api = Api(app, errors=Flask.errorhandler)

This enabled me to change from this before implementation:

{
    "message": "Internal Server Error"
}

To this post implementation:

{
    "description": "Request does not contain an access token",
    "error": "Authorization Required",
    "status_code": 401
}

@vimalloc
Copy link
Owner

vimalloc commented Oct 7, 2021

Thanks @mxhr I will link this solution back to the main 401 errors not returned page! 👍

@d-rocham
Copy link

@mxhr that's the perfect fix, and much more simple. Thanks a lot!

manoedinata added a commit to RobotikaSmaboy/RoDash that referenced this issue Jul 6, 2023
@ayinmursalin
Copy link

@paurakhsharma

I think I found a workaround to integrate custom errors. Here's my current setup: Set PROPAGATE_EXCEPTIONS = False Then overwrite the error handler flask_restful.Api object as such:

from flask import Blueprint
from flask_restful import Api as _Api

# some custom errors
class RoomDoesNotExist(Exception):
    pass

CUSTOM_ERRORS = {
'RoomDoesNotExist': {
        'message': "A room by that name does not exist.",
        'status': 404,
    },
}

class Api(_Api):
    def error_router(self, original_handler, e):
        """ Override original error_router to only custom errors and parsing error (from webargs)"""
        error_type = type(e).__name__.split(".")[-1] # extract the error class name as a string
        # if error can be handled by flask_restful's Api object, do so
        # otherwise, let Flask handle the error
        # the 'UnprocessableEntity' is included only because I'm also using webargs
        # feel free to omit it
        if self._has_fr_route() and error_type in list(CUSTOM_ERRORS) + ['UnprocessableEntity']:
            try:
                return self.handle_error(e)
            except Exception:
                pass  # Fall through to original handler

        return original_handler(e)

api_bp = Blueprint('api', __name__)
api = Api(api_bp, errors=CUSTOM_ERRORS)

This way, flask_restful's errors are handled by flask_restful's Api object, and other errors fall through to Flask's error handler.

This is the best solutions for Flask-Restful and Flask-JWT-Extended in Production
I also try the same, and don't need to add CUSTOM_ERRORS it still works

class CustomApi(_Api):
    def error_router(self, original_handler, e):
        return original_handler(e)

and in resource you can simply add

auth_blueprint = Blueprint("auth", __name__, url_prefix="/api")
auth_api = CustomApi(auth_blueprint)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants