Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get Flask optional URL parameters in a decorator?

Tags:

python

flask

I have Flask APIs all use phone as optional url parameters as showed below, and I want to use a decorator to verify if the phone number is correct. So can I get the parameter "phone" somewhere without parsing the request url?

@user_api.route("/<phone>/login")
@check_phone
def login(phone):
    f = OrderedDict()
    f['error'] = 0
    return jsonify(f)

@user_api.route("/<phone>/logout")
@check_phone
def logout(phone):
    f = OrderedDict()
    f['error'] = 0
    return jsonify(f)
like image 740
chishui Avatar asked May 18 '15 23:05

chishui


1 Answers

There is a better mechanism to validate url values built in to Werkzeug (and Flask). Define a converter and use it like you would use any other converter in the route (for example, <int:id>).

from werkzeug.routing import BaseConverter, ValidationError

class PhoneConverter(BaseConverter):
    regex = r'\d{7,10}'  # this validates the basic form of the value

    def to_python(self, value):
        # do more complicated validation
        if not complicated_phone_validation(value):
            raise ValidationError('not a valid phone number')

        return value

app.url_map.converters['phone'] = PhoneConverter

@app.route('/<phone:phone>')
def get_phone(phone):
    # phone is valid

You can also use a before_request function to validate all routes with a phone argument without having to decorate them all.

from flask import request, abort

@app.before_request
def valid_phone():
    if 'phone' not in request.view_args:
        return  # view has no phone arg

    if not complicated_phone_validation(request.view_args['phone']):
        abort(404)

@app.route('/<phone>')
def get_phone(phone):
    # phone is valid

@app.route('/<other>')
def get_other(other):
    # no phone arg, no validation

If you really want to use a decorator, a decorated function gets called with arguments.

from functools import wraps

def check_phone(f):
    @wraps(f)
    def inner(**kwargs):
        phone = kwargs['phone']
        # do some validation
        return f(**kwargs)

    return inner

@app.route('/<phone>')
@check_phone
def get_phone(phone):
    # phone is valid

    
like image 197
davidism Avatar answered Sep 26 '22 01:09

davidism