pythonpython-3.xflaskwebargs

Validate Variable Parameters In URL Path and Query Parameters using Webargs Flaskparser


I'm trying to validate all data being sent to my api. My url structure contains a variable within it, /api/v2/users/<string:username>/collections/, as well as actual query string parameters, all of which need to be passed through validation.

The full url looks like this: https://127.0.0.1:5000/api/v2/users/<string:username>/collections/?page=5&per_page=10

The two variables that need to be validated are: username page, and per_page. The easy solution would be to change my url structure, but I'm wondering if it's possible to accomplish what I need and keep the simplicity that I currently have without adding additional validation within my resource class. If it's possible, how do you do it?

class UserCollections(Resource):
    @use_args({
        'username': fields.Str(
            required=True,
            validate=username_length,
            error_messages=dict(
                required='Username is required.',
                validator_failed='Username can be between 3 and 25 characters.',
            )
        ),
        'page': fields.Int(
            #required=True,
            missing=1,
            validate=feed_minmax_pages,
            error_messages=dict(
                validator_failed='Maximum number of pages reached.',
            )
        ),
        'per_page': fields.Int(
            #required=True,
            missing=5,
            validate=validate.Range(min=5,max=25),
            error_messages=dict(
                validator_failed='Test number of pages reached.',
            )
        ),
    }, locations=('query',))

    def get(self, args, username):
        print(args)
        print(username)

        return default_schema(
            data={},
            http_status=200
        )

When I run the code, I get a validation error for username because it doesn't exist in the args.


Solution

  • After poking around for a while I came across the solution, which I now see in the webargs flaskparser documentation

    This can easily be used in addition to other use_args location params within the arguments. It appears that the request method function get, post, etc still requires that you pass in that url variable. In my case it was <username>.

    class UserCollections(Resource):
        @use_args({
            'username': fields.Str(
                location='view_args',
                required=True,
                validate=username_length,
                error_messages=dict(
                    required='Username is required.',
                    validator_failed='Username can be between 3 and 25 characters.',
                )
            ),
            'page': fields.Int(
                location='query',
                missing=1,
                validate=feed_minmax_pages,
                error_messages=dict(
                    validator_failed='Maximum number of pages reached.',
                )
            ),
            'per_page': fields.Int(
                location='query',
                missing=5,
                validate=validate.Range(min=5,max=25),
                error_messages=dict(
                    validator_failed='Test number of pages reached.',
                )
            ),
        })
    
        def get(self, args, username):
            print(args) # access with args['username']
            print(username) # would be nice to not have a second of the same var
    
            return default_schema(
                data={},
                http_status=200
            )