flask-restful / flask-restful

Simple framework for creating REST APIs
http://flask-restful.readthedocs.io
BSD 3-Clause "New" or "Revised" License
6.83k stars 1.04k forks source link

Handle TypeError by reqparse with bundle errors enabled. #453

Open grucin opened 9 years ago

grucin commented 9 years ago

There is an unexpected behavior when type of argument in request doesn't fit with that declared in add_argument method, e.g. you have argument:

parser.add_argument('foo', type=list, location='json')

and request json is something like:

{"foo": 1}

In that case Argument.convert raise TypeError, which will not be catched because RequestParser.parse_args handle only error of ValueError instance. The TypeError message will be added as a value to argument namespace.

intentionally-left-nil commented 9 years ago

From my understanding of the codebase, the convert() function is supposed to (re)raise exceptions during conversion. This is handled in Argument.parse() which throws the 400 response to the user:

                        value = self.convert(value, operator)
                    except Exception as error:
                        if self.ignore:
                            continue
                        self.handle_validation_error(error)
grucin commented 9 years ago

I forgot to add that this is happening when bundle_errors=True. In that case method Argument.handle_validation_error will not call abort, but will return error and msg

    if current_app.config.get("BUNDLE_ERRORS", False) or bundle_errors:
        msg = {self.name: "%s" % (error_msg)}
        return error, msg
    msg = {self.name: "%s" % (error_msg)}
    flask_restful.abort(400, message=msg)

And this piece of code forwards the TypeError to args namespace:

    for arg in self.args:
        value, found = arg.parse(req, self.bundle_errors)
        if isinstance(value, ValueError):
            errors.update(found)
            found = None
        if found or arg.store_missing:
            namespace[arg.dest or arg.name] = value
minhoryang commented 9 years ago

+1 happened with app.config['PROPAGATE_EXCEPTIONS'] = True

Traceback (most recent call last):
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask_restful/__init__.py", line 270, in error_router
    return original_handler(e)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/_compat.py", line 32, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask_restful/__init__.py", line 270, in error_router
    return original_handler(e)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/_compat.py", line 32, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask_restful/__init__.py", line 462, in wrapper
    resp = resource(*args, **kwargs)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask_restful/__init__.py", line 572, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/Volumes/Unsigned/server/application/models/user.py", line 55, in post
    args = self.wanted.parse_args()
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask_restful/reqparse.py", line 295, in parse_args
    value, found = arg.parse(req, self.bundle_errors)
  File "/usr/local/var/lib/pyenv/versions/3.4.2-server/lib/python3.4/site-packages/flask_restful/reqparse.py", line 165, in parse
    if name in source:
TypeError: argument of type 'int' is not iterable